【问题标题】:setState not updating asynchronouslysetState 不异步更新
【发布时间】:2020-01-31 21:44:32
【问题描述】:

这是一个使用反应钩子的功能反应组件 - 状态正在以一种奇怪的方式更新,破坏了我的组件流程。关于为什么会发生这种情况以及如何可靠地更新子组件所需的状态的任何想法。目前,我的 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

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

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

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

这带走了 this.setState 功能,并且不确定使用什么解决方法来获取状态以在 var 可用、uid 等之后立即存储它......

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

【问题讨论】:

    标签: reactjs react-hooks


    【解决方案1】:

    问题仍然是为什么 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 */
    };
    

    【讨论】:

    • "如果您想要一个值,您可以在 useEffect 中更改并在之后立即读取,请使用 const data = useRef 并将 data.current 字段更改为您想要的值。"您能否展示一个值被添加到状态 onmount 并立即可用于子组件等的示例?
    • 感谢您的示例。 useref 比 usestate 更好吗?我需要数据成为渲染时子组件使用的状态的一部分。特别是,我需要进行 window.location.search 解析,这似乎只有在组件安装之后才有可能。我想知道 setState 在第一次渲染之前捕获它的最佳方法是什么?
    • "调用 setData 的目的是在下一次重新渲染时获取新的数据值。"不确定我是否在关注你。你看到上面的console.logs了吗? data.uid 在渲染之前打印用户 ID 低于 4 并且 before 它在 3 下再次打印 undefined ...
    【解决方案2】:

    代码有几个问题:

    • setData 不返回承诺。实际上它不返回任何东西。所以在上面使用await是没有效果的。
    • 在效果函数中,您正在调用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]);
    

    【讨论】:

    • 谢谢,我只需要通过添加一些数据来确保 setData 正在更新状态,比如这里的 uid。然后,该数据应该可用,以防某个函数需要将其写入数据库,作为诸如预订之类的事务的一部分。我发现与基于类的 this.setState({smth:smth}) 完全不同,我可以依靠它来立即更新状态,功能性的不是那么可靠 - 例如,这不会更新状态setData({...data, smth:smth}) 使 data.smth 为非空并准备好发送等,这似乎是一个缺陷和需要解决方法
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-15
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2020-10-28
    相关资源
    最近更新 更多