setTimeout的this.state与useState [英] setTimeout for this.state vs useState

查看:107
本文介绍了setTimeout的this.state与useState的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我使用类组件时,我有代码:

When I use class component, I have code:

setTimeout(() => console.log(this.state.count), 5000);

当我使用钩子时:

const [count, setCount] = useState(0);
setTimeout(() => console.log(count), 5000);

如果我触发 setTimeout ,然后在超时( 5000ms )之前将 count 更改为1,则类组件将 console.log(1)(最新值),对于 useState ,它是 console.log(0)(寄存器超时时的值).
为什么会这样?

If I trigger setTimeout then change the count to 1 before the timeout (5000ms), class component will console.log(1) (the newest value), and for useState it is console.log(0) (value when register timeout).
Why does this happen?

推荐答案

更新版本:

问题: 功能的settimeout / setInterval 中的React State变量的行为差异>类组件?

Question: Difference in behavior of a React State variable inside setTimeout / setInterval for function and class components?

案例1 :函数组件中的状态变量(过时关闭):

Case 1: State variable in function component (stale closure):

const [value, setValue] = useState(0)

useEffect(() => {
  const id = setInterval(() => {
    // It will always print 0 even after we have changed the state (value)
    // Reason: setInterval will create a closure with initial value i.e. 0
    console.log(value)
  }, 1000)
  return () => {
    clearInterval(id)
  }
}, [])

案例2 :类组件中的状态变量(没有过期):

Case 2: State variable in class component (no stale closure):

constructor(props) {
  super(props)
  this.state = {
    value: 0,
  }
}

componentDidMount() {
  this.id = setInterval(() => {
    // It will always print current value from state
    // Reason: setInterval will not create closure around "this"
    // as "this" is a special object (refernce to instance)
    console.log(this.state.value)
  }, 1000)
}

案例3 :让我们尝试围绕 this

// Attempt 1

componentDidMount() {
  const that = this // create a local variable so that setInterval can create closure
  this.id = setInterval(() => {
    console.log(that.state.value)
    // This, too, always print current value from state
    // Reason: setInterval could not create closure around "that"
    // Conclusion: Oh! that is just a reference to this (attempt failed)
  }, 1000)
}

案例4 :让我们再次尝试在类组件中创建陈旧的闭包

Case 4: Let's again try to create a stale closure in class component

// Attempt 2

componentDidMount() {
  const that = { ...this } // create a local variable so that setInterval can create closure
  this.id = setInterval(() => {
    console.log(that.state.value)
    // Great! This always prints 0 i.e. the initial value from state
    // Reason: setInterval could create closure around "that"
    // Conclusion: It did it because that no longer is a reference to this,
    // it is just a new local variable which setInterval can close around
    // (attempt successful)
  }, 1000)
}

案例5 :让我们再次尝试在类组件中创建陈旧的闭包

Case 5: Let's again try to create a stale closure in class component

// Attempt 3

componentDidMount() {
  const { value } = this.state // create a local variable so that setInterval can create closure
  this.id = setInterval(() => {
    console.log(value)
    // Great! This always prints 0 i.e. the initial value from state
    // Reason: setInterval created closure around value
    // Conclusion: It is easy! value is just a local variable so it will be closed
    // (attempt successful)
  }, 1000)
}

案例6 :班级获胜(无需付出过多努力来避免过时的关闭).但是,如何在功能组件中避免使用?

Case 6: Class has won (no extra effort to avoid the stale closure). But, how to avoid it in function component?

// Let's find solution

const value = useRef(0)

useEffect(() => {
  const id = setInterval(() => {
    // It will always print the latest ref value
    // Reason: We used ref which gives us something like an instance field.
    // Conclusion: So, using ref is a solution
    console.log(value.current)
  }, 1000)
  return () => {
    clearInterval(id)
  }
}, [])

源1 案例6 :让我们找到功能组件的另一种解决方案

Case 6: Let's find another solution for function components

useEffect(() => {
  const id = setInterval(() => {
    // It will always print the latest state value
    // Reason: We used updater form of setState (which provides us latest state value)
    // Conclusion: So, using updater form of setState is a solution
    setValue((prevValue) => {
      console.log(prevValue)
      return prevValue
    })
  }, 1000)
  return () => {
    clearInterval(id)
  }
}, [])


原始版本:

该问题是由闭包引起的,可以使用 ref 进行修复.但是,这是解决该问题的一种解决方法,即使用"updater"访问最新的 state 值.setState 的形式:

The issue is caused by closures and can be fixed by using ref. But here is a workaround to fix it i.e. access the latest state value using "updater" form of setState:

function App() {

  const [count, setCount] = React.useState(0);

  React.useEffect(() => {
    setTimeout(() => console.log('count after 5 secs: ', count, 'Wrong'), 5000)
  }, [])

  React.useEffect(() => {
    setTimeout(() => {
      let count
      setCount(p => { 
        console.log('p: ', p)
        count = p
        return p
       })
      console.log('count after 5 secs: ', count, 'Correct')
    }, 5000);
  }, [])

  return (<div>
    <button onClick={() => setCount(p => p+1)}>Click me before 5 secs</button>
    <div>Latest count: {count}</div>
  </div>)
}

ReactDOM.render(<App />, document.getElementById('mydiv'))

<script crossorigin src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<body>
<div id="mydiv"></div>
</body>

这篇关于setTimeout的this.state与useState的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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