【问题标题】:Async function in react doesn't react to state change反应中的异步功能不会对状态变化做出反应
【发布时间】:2021-06-23 19:38:44
【问题描述】:

我希望我的aysnc function 对状态变化做出反应,以便它可以在状态变化时终止。我发现它对改变状态没有反应。

在提供的示例中,我正在从函数内部更改状态,但是我已经对其进行了测试并更改了状态从函数外部(按钮单击)它没有也可以工作。

const [testVariable, setTestVariable] = useState(false);

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

const testFunction = async () => {
  await timeout(1000);

  console.log("Setting test variable to: " + true);
  setTestVariable(true);

  await timeout(1000);
  
  console.log("Test variable is: " + testVariable);
}

预期的结果是看到变量变为 true,但是我看到的是:

【问题讨论】:

    标签: javascript reactjs


    【解决方案1】:

    状态变量永远不会改变它们的值。 (请注意,您可以、拥有并且应该使用 const 声明它们,因为它们不会改变)。

    组件函数的后续调用将获得一个具有相同名称和不同值的新状态变量。

    您的 testFunction 函数已关闭旧状态变量。


    我怀疑你可以通过参考解决这个问题:

    const [testVariable, setTestVariable] = useState(false);
    const reference = useRef();
    reference.current = testVariable;
    

    ...然后让您的函数测试reference.current 的值而不是testVariable。


    但是,这确实感觉像 XY Problem,其中真正的解决方案是“使用传递给 useEffect 的函数的返回值来停止您想要停止的任何事情”(并使用 testVariable 作为useEffect) 的依赖项。

    【讨论】:

    • 你在使用 TypeScript 吗?
    • @Quentin 是的。抱歉,我已经删除了我的评论。对于那些寻求 TypeScript 解决方案的人 - 你只需要使用useRef<VariableType>(),其中VariableType 是你的useState 持有的任何东西。例如boolean
    • 这不是 useRef - reactjs.org/docs/hooks-reference.html#useref 的正确用例。 useRef 应该用于将有状态变量从子组件传递到父组件。 (例如:一个元素的详细视图有一个对父列表中相同元素的值的引用,以根据更改的属性更新父元素)。
    • @KimBrunJørgensen — 请仔细阅读您刚刚分享的链接。它说“一个常见的用例是强制访问孩子”。共同并不意味着“仅可接受”。它还说“但是,useRef() 不仅仅用于 ref 属性。它可以方便地保留任何可变值”。另请注意我回答的最后一段,我建议可能存在更好的解决方案,但该问题没有提供有关实际问题的足够信息。
    【解决方案2】:

    react 中的状态更新是异步的,这意味着您的状态将在下一个渲染周期中更改。 你可以使用Effect hook来跟踪任何状态是否改变。

    useEffect(() => {
       console.log(testVariable);
    }, [testVariable]);
    

    【讨论】:

    • 是的,我知道这一点,但我需要在状态更改时终止异步函数内的 for
    • 在这种情况下,您应该将 testVariable 的新值存储在 async function 内的 const 中,并将其用作 for 循环的引用。
    【解决方案3】:

    改成

    const [testVariable, setTestVariable] = useState(false);
    
    useEffect(()=> {
      testFunction();
    }, [])
    
    const testFunction = async () => {
      await timeout(1000);
    
      console.log("Setting test variable to: " + true);
    
      let newTestVariable = true;  
      setTestVariable(newTestVariable);
    
      await timeout(1000);
      
      console.log("Test variable is: " + newTestVariable);
    }
    

    更改状态变量是一个异步操作,并与称为事件循环的东西一起工作。当您执行 setTestVariable(true) 时,它不会立即更新。

    【讨论】:

      【解决方案4】:

      async function 正在更改状态,但您在同一生命周期中记录 testVariable,这就是为什么它看起来好像状态没有更改。

      如果您改为将代码更改为

      const [testVariable, setTestVariable] = React.useState(false)
      
      console.log('testVariable', testVariable)
      React.useEffect(() => {
        testFunction()
      }, [])
      
      const testFunction = async () => {
        await timeout(1000)
        setTestVariable(!testVariable)
      }
      
      return <h2>{testVariable}</h2>
      

      它将记录输出:

      testVariable false
      testVariable false
      testVariable true
      testVariable true
      

      它显示false 和true 的两个日志的原因是因为console.log 在React.useEffect(() =&gt; {}, []) 之外(对于有状态的React 组件,它相当于componentDidMount)。 React 在组件挂载之前首先执行console.log,然后在组件挂载之后执行React.useEffect 中的代码并重新渲染组件,从而再次执行console.log 语句。

      仅记录一次,您必须将 React.useEffect 更改为:

      React.useEffect(() => {
        console.log('testVariable', testVariable)
        testFunction()
      }, [testVariable])
      

      向数组参数[testVariable] 添加属性,将React.useEffect 从componentDidMount 等效函数更改为在componentDidMount 和testVariable 的每个新状态上执行的函数聊天。

      【讨论】:

        猜你喜欢
        • 2020-05-18
        • 2020-07-26
        • 2021-06-06
        • 1970-01-01
        • 2021-07-22
        • 1970-01-01
        • 2022-11-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多