useState / useEffect的错误反应传播运算符

时间:2019-10-07 20:39:49

标签: reactjs react-hooks use-effect

我遇到一个问题,我正在使用传播运算符设置状态,但是useEffect中的函数调用只能将从API提取的最后一个值设置为状态。

// get all events to display on load
const getEvents = () => {
    console.log(props.id);
    axios.get(API.EVENTS.ROOT + props.id).then(
        res => {
            const data = res.data;
            setEvent(data);
            console.log(data);
            // event start and end dates
            setStart(new Date(data.start));
            setEnd(new Date(data.finish));
            // breakout dates
            for (let i = 0; i < data.breakouts.length; i++) {
                console.log(data.breakouts[i].start);
                data.breakouts[i].start = new Date(data.breakouts[i].start);
                data.breakouts[i].end = new Date(data.breakouts[i].end);
                console.log(data.breakouts[i].name, "test" + data.breakouts[i].start);
                // set date picker initial value 
                setFields({
                    ...fields,
                    [data.breakouts[i].name]: data.breakouts[i].start
                })
                console.log(fields);
            }

            setBreakoutRow(data.breakouts);
            console.log(breakoutRow);

        }
    ).catch(err => {
        console.log(err);
    })
}
// get events on load
useEffect(() => {
    console.log(props);
    getEvents();
}, []);

我添加了字段状态变量或getEvents函数作为依赖项,它确实更新了所有值的state字段,但是却导致死循环无限。这给了我最后一个映射行中空白的日期。

另一点需要注意的是,在页面加载之后,可以通过函数调用来更新字段对象,就像这样:

const handleDateChange = (dateName, dateValue) => {
        console.log(dateName, dateValue);
        setFields({
            ...fields,
            [dateName]: dateValue
        })
        console.log(fields);
    }

这使我相信我的useEffect钩子有问题,但是我不确定它出了什么问题。

2 个答案:

答案 0 :(得分:3)

原因是您访问上一个值以添加新元素的方式:

for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  setFields({
    ...fields,
    [data.breakouts[i].name]: data.breakouts[i].start
  })
}

由于fields仅在下一次渲染时更新,因此您要向数组中添加单个元素(但每个循环都是不同的元素)。

没有钩子/反应的情况也一样:

const start = [];
for(let i = 0; i< 5; i++) console.log([...start, i]);

这将永远不会输出[0,1,2,3,4]

你能做什么

选项1.在中间变量中收集数据:

const temp = {};
for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  data.breakouts[i].end = new Date(data.breakouts[i].end);
  temp[data.breakouts[i].name] = data.breakouts[i].start
}
setFields({
  ...fields,
  ...temp
});

选项2。使用setter的功能版本作为累加器:

for (let i = 0; i < data.breakouts.length; i++) {
  data.breakouts[i].start = new Date(data.breakouts[i].start);
  setFields(({fields: prevFields}) => ({
    ...prevFields,
    [data.breakouts[i].name]: data.breakouts[i].start
  }))
}

出于两个原因,我更喜欢第一选择:

  1. 最好最终将数据转换移到调用点附近,所以需要axios.get而不是API.getMeaningfullThings返回结构;因此更容易重构
  2. 稍微多一点的代码可以更好地说明正在发生的事情(这只是转换,而不是过滤或任何特定于React的事情)

答案 1 :(得分:2)

如果我的理解正确,那么您在第一个示例中遇到setState的问题。原因是您正在循环内调用setState,并且试图传播旧状态,但是在循环完成之前该状态未设置,因此您必须累积所有要在循环中进入您的状态并将其设置在末尾。