setState不异步更新

时间:2019-10-02 15:15:19

标签: reactjs react-hooks

这是一个使用react钩子的功能性react组件-状态以一种奇怪的方式更新,破坏了我的组件流。关于为什么发生这种情况以及如何可靠地更新子组件所需状态的任何想法。目前,我的Firestore保存无法正常运行,因为它需要用户ID,并且由于更新滞后而无法从状态中获取它。

const XYZ = (props) => {

  const [data, setData] = useState({
    ...props,
  });

  const getinitdata = async () => {
    let user = await utils.getUser();
    console.log("1", user.uid); //USERID
    await setData({ ...data, uid: user.uid });
    console.log("3", data.uid); //UNDEFINED
  }

  useEffect(() => {
    console.log("componentDidMount");
    getinitdata();
    console.log("2", data.uid); //UNDEFINED
    return () => {
      console.log("componentWillUnmount");
    };
  }, []); // empty-array means don't watch for any updates

  console.log("4", data.uid); //USERID

  return (

    <div>{data.uid}</div>


  );
}

上面的console.log的打印如下:

componentDidMount
2 undefined
1 KMDTFXxktUTwq6Kg34l8r5qWkAL
4 KMDTFXxktUTwq6Kg34l8r5qWkAL
3 undefined

目标只是通过添加一些数据(例如此处的uid)来确保setData正在更新状态。因此,将状态用作临时存储。

然后,如果函数需要将其作为预订等事务的一部分写入数据库,则应该可以使用此数据。

我发现它与基于类的this.setState({smth:smth})完全不同,我可以依靠它立即更新状态,而不能依赖于功能上的状态-例如,这不会将data.smth的状态setData({... data,smth:smth})更新为非空并准备发送。

这脱离了this.setState功能,并且不确定使用哪种变通办法来获取状态以在var可用后立即存储var,uid等...

任何关于如何做到这一点的想法都将受到欢迎。

2 个答案:

答案 0 :(得分:0)

  

问题仍然是为什么console.log(“ 3”,data.uid);尽管在setData之前等待,但仍未定义。

您在useEffect中的箭头函数捕获 data变量,并且调用setData不会更新该函数中data的值,是否使用await。怎么可能data被声明为const,因此任何看到它的都将获得初始值。调用setData的目的是在下一次重渲染时获得新值data

如果您想要一个值,可以在useEffect中进行更改并在之后立即读取,请使用const data = useRef并将data.current字段更改为所需的值。

P.S。,您通常是shouldn't copy props to state

编辑:提供的示例

const XYZ = props => {
  const data = useRef(null);
  useEffect(() => {
    const fn = async () => {
      data.current = 1;
      await delay(1000);
      data.current = 2;
      console.log(data.current); // 2
    };
    fn();
  }, []);
  return /* whatever */
};

答案 1 :(得分:0)

代码有两个问题:

  • setData不返回承诺。实际上,它不返回任何内容。因此,对它使用await无效。
  • 在effect函数中,您正在调用getInitialData,这是一个异步函数,但您没有等待其完成。

假设您要在挂载时获取用户ID,然后在用户ID可用后执行一些操作,您可以执行以下操作:

const getInitialData = async () => {
    let user = await utils.getUser();
    setData({ ...data, uid: user.uid });
}

useEffect(() => {
    getInitialData();   
}, []);

useEffect(() => {
    if(data.uid) {
         // whatever needs to be done once uid is available.
    }
}, [data.uid]);