useEffect 的回调的 return 语句什么时候执行? [英] When does the useEffect's callback's return statement execute?

查看:43
本文介绍了useEffect 的回调的 return 语句什么时候执行?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想澄清我对这里发生的事情的理解.任何能改善我目前理解的细节都将不胜感激.

function Timer() {让 [time, setTime] = useState(5);useEffect(() => {让计时器 = setInterval(() => {设置时间(时间 - 1);}, 1000)返回 () =>清除间隔(定时器);}, );返回 

{time}

}导出默认定时器

https://codesandbox.io/s/cranky-chaplygin-g1r0p

  1. time 正在初始化为 5.
  2. useEffect 被读取.它的回调必须准备好稍后触发.
  3. 呈现div.
  4. useEffect 的回调被执行.setInterval 的回调准备好触发.useEffectreturn 语句肯定不会在这里触发,因为如果它触发了它会取消计时器(并且计时器确实有效).
  5. 大约 1 秒后,setInterval 的回调将触发,将 time 的状态更改为 4.
  6. 现在一个状态发生了变化,函数被重新执行.time 是一个新变量,它被初始化为新的时间状态.
  7. 一个新的 useEffect 被读取,它的回调准备稍后触发.(发生这种情况是因为 useEffect() 没有第二个参数).
  8. 执行组件函数的return 语句.这有效地重新渲染了 div.
  9. 在某些时候,前一个 useEffectreturn 语句会执行(这会禁用前一个 useEffect 中的 timer代码>).我不确定何时会发生这种情况.
  10. 'new' useEffect 的回调被执行.

解决方案

您对事件顺序的理解是正确的.唯一缺少的是效果回调和清理的精确时间.

当组件重新渲染时,任何 useEffect 都将分析其依赖数组以进行更改.如果有变化,那么效果回调将运行.这些回调保证按照它们在组件中声明的顺序运行.例如,下面的 a 总是在 b 之前被记录.

const App = () =>{const [num, setNum] = React.useState(0);React.useEffect(() => {setInterval(() => {setNum(num => num + 1);}, 1000);}, []);React.useEffect(() => {console.log('a', num);}, [编号]);React.useEffect(() => {console.log('b', num);}, [编号]);返回编号;}ReactDOM.render(, document.querySelector('.react'));

<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script><div class='react'></div>

这些效果回调将在浏览器重新绘制后不久运行.

现在将效果清理回调添加到组合中.这些将始终同步运行就在渲染运行的效果回调之前.例如,假设组件从 Render A 开始,并且在 Render A 中,效果挂钩返回了清理回调.然后,一些状态发生变化,并发生到渲染 B 的转换,并且存在一个 useEffect 和一个包含状态变化的依赖数组.将会发生的事情是:

  • 功能组件将使用新的 props/state 被调用,用于渲染 B
  • 组件在函数末尾返回新的标记
  • 如有必要,浏览器会重新绘制屏幕
  • 渲染 A 的清理功能将运行
  • 来自渲染 B 的效果回调 将运行

您可以查看最后两个操作的源代码 这里:

commitHookEffectListUnmount(Passive$1 | HasEffect,finishedWork);commitHookEffectListMount(Passive$1 | HasEffect,finishedWork);

第一次调用会调用先前渲染中的所有清理回调.第二个调用调用当前渲染的所有效果回调.当前渲染效果回调在执行先前的渲染清理回调之后同步运行.

I'd like to clarify my understanding of what's happening here. Any detail to improve my current understanding'd be appreciated.

function Timer() {

    let [time, setTime] = useState(5);

    useEffect(() => {
        let timer = setInterval(() => {
          setTime(time - 1);
        }, 1000)
        return () => clearInterval(timer);
    }, );

    return <div>{time}</div>
}

export default Timer

https://codesandbox.io/s/cranky-chaplygin-g1r0p

  1. time is being initialised to 5.
  2. useEffect is read. Its callback must be made ready to fire later.
  3. The div is rendered.
  4. useEffect's callback is executed. setInterval's callback gets ready to fire. Surely useEffect's return statement doesn't fire here, because if it did it would cancel the timer (and the timer does work).
  5. After, roughly, 1 second, setInterval's callback fires changing the state of time (to 4).
  6. Now that a piece of state has changed, the function is re-executed. time, a new variable, is initialised to the new time state.
  7. A new useEffect is read, it's callback made ready to fire later. (This happens because there is no 2nd argument of useEffect()).
  8. The component function's return statement is executed. This effectively re-renders the div.
  9. At some point, the previous useEffect's return statement executes (which disables the timer in that previous useEffect). I'm not sure when this occurs.
  10. The 'new' useEffect's callback is executed.

解决方案

Your understanding of the sequence of events is correct. The only thing missing is the precise timing of the effect callbacks and cleanup.

When the component re-renders, any useEffects will have their dependency arrays analyzed for changes. If there has been a change, then that effect callback will run. These callbacks are guaranteed to run in the order that they're declared in the component. For example, below, a will always be logged just before b.

const App = () => {
    const [num, setNum] = React.useState(0);
    React.useEffect(() => {
      setInterval(() => {
        setNum(num => num + 1);
      }, 1000);
    }, []);
    React.useEffect(() => {
      console.log('a', num);
    }, [num]);
    React.useEffect(() => {
      console.log('b', num);
    }, [num]);
    return num;
}

ReactDOM.render(<App />, document.querySelector('.react'));

<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>

These effect callbacks will run shortly after the browser re-paints.

Now add the effect cleanup callback into the mix. These will always run synchronously just before the effect callback for a render runs. For example, let's say the component starts at Render A, and in Render A, an effect hook has returned a cleanup callback. Then, some state changes, and a transition to Render B occurs, and there exists a useEffect with a dependency array that includes the state change. What will happen is:

  • The functional component will be called with the new props/state, for Render B
  • The component returns the new markup at the end of the function
  • The browser repaints the screen if necessary
  • The cleanup function from render A will run
  • The effect callback from render B will run

You can see the source code for those last two actions here:

commitHookEffectListUnmount(Passive$1 | HasEffect, finishedWork);
commitHookEffectListMount(Passive$1 | HasEffect, finishedWork);

That first call invokes all cleanup callbacks from a prior render. That second call invokes all effect callbacks for the current render. Current render effect callbacks run synchronously after the execution of prior render cleanup callbacks.

这篇关于useEffect 的回调的 return 语句什么时候执行?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆