为了账号安全,请及时绑定邮箱和手机立即绑定

ReactJS useState hook 实际值在异步承诺中已过时

ReactJS useState hook 实际值在异步承诺中已过时

绝地无双 2023-11-02 21:00:13
在我的反应函数组件中,我发送多个服务器请求并通过异步承诺更新状态挂钩值,方法是将服务器结果附加到状态最新值,但是一旦创建承诺,状态挂钩的值不会在运行的承诺内更新,因此如果另一个Promise 更新状态值,其他正在运行的 Promise 不会收到通知,因此它们使用旧版本的状态来进行进一步的状态更新。下面的代码是我的组件的简化版本,我希望在多个 Promise 运行且状态更新时,从每个 Promise 的第 19 行控制台日志中看到相同(且最新)的值。function App() {  const [myArray, setMyArray] = useState([0,1,2])  const sleep = (ms:number) => {    return new Promise(resolve => setTimeout(resolve, ms));  }  const updateArray = () => {    setMyArray([...myArray, myArray.length])  }  const firePromise = () => {    new Promise(async (resolve) => {      const timeStamp = new Date().getTime()      let repeatTime = 0      while(repeatTime < 12){        console.log("array: ", myArray, "promiseIdenifier: ", timeStamp);        repeatTime += 1        await sleep(1000)      }      resolve({timeStamp, myArray})    }).then(val => {      console.log("resolved: ", val);          }).catch(err => {      console.log("rejected: ", err);          })  }  return (    <div className="App">      <button onClick={firePromise}>new promise</button>      <button onClick={updateArray}>updateArray</button>    </div>  );}export default App;  
查看完整描述

1 回答

?
桃花长相依

TA贡献1860条经验 获得超8个赞

一旦 React 组件被渲染,就可以假设组件的当前“状态”像快照一样存在。


console.log 中的“myArray”是创建 firePromise 时的“myArray”。所以保留第一个值是正确的。(每次渲染组件时,都会创建一个新的 firePromise。)


有一种方法。第一个是使用 ref,第二个是使用 setState。


第一的


function App() {

  const myArray = useRef<Array<number>>([0, 1, 2]);

  const sleep = (ms: number) => {

    return new Promise((resolve) => setTimeout(resolve, ms));

  };

  const updateArray = () => {

    myArray.current.push(myArray.current.length);

  };

  const firePromise = () => {

    new Promise(async (resolve) => {

      const timeStamp = new Date().getTime();

      let repeatTime = 0;

      while (repeatTime < 12) {

        console.log(

          "array: ",

          myArray.current,

          "promiseIdenifier: ",

          timeStamp

        );

        repeatTime += 1;

        await sleep(1000);

      }

      resolve({ timeStamp, myArray: myArray.current });

    })

      .then((val) => {

        console.log("resolved: ", val);

      })

      .catch((err) => {

        console.log("rejected: ", err);

      });

  };

  return (

    <div className="App">

      <button onClick={firePromise}>new promise</button>

      <button onClick={updateArray}>updateArray</button>

    </div>

  );

}


export default App;

第二


function App() {

  const [myArray, setMyArray] = useState([0, 1, 2]);

  const sleep = (ms: number) => {

    return new Promise((resolve) => setTimeout(resolve, ms));

  };

  const updateArray = () => {

    setMyArray([...myArray, myArray.length]);

  };

  const firePromise = () => {

    new Promise(async (resolve) => {

      const timeStamp = new Date().getTime();

      let repeatTime = 0;

      while (repeatTime < 12) {

        setMyArray((prevMyArray) => {

          console.log("array: ", prevMyArray, "promiseIdenifier: ", timeStamp);

          return prevMyArray;

        });

        repeatTime += 1;

        await sleep(1000);

      }

      setMyArray((prevMyArray) => {

        resolve({ timeStamp, prevMyArray });

        return prevMyArray;

      });

    })

      .then((val) => {

        console.log("resolved: ", val);

      })

      .catch((err) => {

        console.log("rejected: ", err);

      });

  };

  return (

    <div className="App">

      <button onClick={firePromise}>new promise</button>

      <button onClick={updateArray}>updateArray</button>

    </div>

  );

}


export default App;

将回调传递给 setState 函数时,当前状态将作为第一个参数传递。这是使用这个的快捷方式。


建议使用当值改变时视图应该改变的值作为状态。更改“myArray”不会影响视图,因此使用 ref 是正确的方法。


查看完整回答
反对 回复 2023-11-02
  • 1 回答
  • 0 关注
  • 115 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信