react useState 异步回调取不到最新值?见代码

  const [fields, setfields] = useState([0]);

  function change(){
    setTimeout(()=>{
      setfields([...fields, 1])
    },100)
    setTimeout(()=>{
      setfields([...fields, 2])
    },500)
  }
  
  useEffect(() => {
    console.log(123,fields )
  }, [fields]);
<Button onClick={change}>change</Button>

image.png

如何实现输出[0, 1, 2]

阅读 37.5k
4 个回答

原因其实在 react 的官方文档里面有提到

组件内部的任何函数,包括事件处理函数和 effect,都是从它被创建的那次渲染中被「看到」的。

也就是组件内部的函数拿到的总是定义它的那次渲染中的props和state,文档里面也提到了解决办法:

解决办法1

如果你刻意地想要从某些异步回调中读取_最新的_state,你可以用一个 ref来保存它,修改它,并从中读取。

你的代码修改之后:

const [fields, setfields] = React.useState([0]);
const intervalRef = React.useRef();
function change(){
    setTimeout(()=>{
      setfields([...intervalRef.current, 1])
    },100)
    setTimeout(()=>{
      setfields([...intervalRef.current, 2])
    },500)
}

React.useEffect(() => {
    intervalRef.current = fields;
    console.log(123,fields )
}, [fields]);

相关文档链接:为什么我会在我的函数中看到陈旧的 props 和 state ?
也推荐一篇解析文章,很清晰得解析了这个问题:useEffect 完整指南

解决办法2

第二个解决办法就是楼上提到的方法,文档中是这么说的。

如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给setState。该函数将接收先前的 state,并返回一个更新后的值。

相关文档链接:函数式更新

你的 setFields 在闭包中,会从闭包作用域取 fields 的值。解决办法是使用函数当作 setFields 的参数。

setFields(fields => [...fields, 1])

const promise = new Promise();
promise.then(()=>{
const obj = {...state, ...item}
setState(obj)
return obj;
}).then((obj)=>{
setState({...obj, item2})
})

用useState的函数式更新, 代码改成

const [fields, setfields] = useState([0]);

  function change(){
    setTimeout(()=>{
      setfields([...fields, 1])
    },100)
    setTimeout(()=>{
      setfields(fields => [...fields, 2])
    },500)
  }
  
  useEffect(() => {
    console.log(123,fields )
  }, [fields]);
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏