javascript - this.state 与 useState 的 setTimeout

标签 javascript reactjs react-hooks

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

setTimeout(() => console.log(this.state.count), 5000);
当我使用钩子(Hook)时:
const [count, setCount] = useState(0);
setTimeout(() => console.log(count), 5000);
如果我触发 setTimeout然后更改 count在超时( 5000ms )之前为 1,类组件将 console.log(1) (最新值),对于 useState它是 console.log(0) (注册超时时的值)。
为什么会这样?

最佳答案

更新版本:
问题: setTimeout 中 React State 变量的行为差异/setInterval对于 功能 类(class) 成分?
案例一 :函数组件中的状态变量(陈旧的闭包):

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)
  }
}, [])
案例二 :类组件中的状态变量(没有陈旧的闭包):
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 :让我们再次尝试在类组件中创建一个陈旧的闭包
// 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 :让我们再次尝试在类组件中创建一个陈旧的闭包
// 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 : 类赢了 (没有额外的努力来避免过时的关闭)。但是,函数组件中如何避免 ?
// 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)
  }
}, [])
source-1 , source-2
案例6 : 找另一种解决方案用于功能组件
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 来解决。 .但这里有一个解决方法,即访问最新的 state使用 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>

关于javascript - this.state 与 useState 的 setTimeout,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65253665/

相关文章:

javascript - 尝试编写一个仅匹配数字、空格、括号、+ 和 - 的正则表达式

javascript - 有没有办法衡量页面中 Javascript 的权重?

javascript - native JavaScript 语法中的千位分隔符

reactjs - 在我的 React 应用程序中使用 @emotion/core 时出错

javascript - 如何在特定节点上安装嵌套的 react 组件?

javascript - 无法使用 derbyjs 在 View 中显示文档列表

javascript - undefined is not a function (evaluating '_this2.closeDrawer()') react native app 错误

reactjs - 初始化时跳过 Hook 更改 (useEffect)

reactjs - 更改值后 React 组件不更新

javascript - 如何使用 React Hooks API 管理异步函数