我正在修复我公司的应用程序的FE代码,该应用程序仍然使用React 17,我注意到设置React 17和React 18之间的状态时有不同的行为(没有严格模式).

代码:

import React, { useEffect, useState } from 'react';

export default function App(props) {
  const [state, setState] = useState(0);

  console.log('render', state);

  useEffect(() => {
    (async () => {
      for (let i = 0; i < 3; i++) {
        await new Promise((res) => {
          setTimeout(res, 500);
        })

        console.log('before set state');
        setState(val => val + 1);
        console.log('after set state');
      }
    })();
  }, []);

  return (
    <h1>{state}</h1>
  );
}

控制台输出:

React 17

render 0
before set state
render 1
after set state
before set state
render 2
after set state
before set state
render 3
after set state

React 18

render 0
before set state
after set state
render 1
before set state
after set state
render 2
before set state
after set state
render 3

似乎在React 17中,setState同步触发了重新渲染,这解释了为什么我们在控制台输出after set state之前看到render {num}.然而,在React 18中,我们可以看到重新呈现发生在控制台before set stateafter set state之后,这意味着setState没有立即触发重新呈现.

有人能解释一下这里发生了什么吗?这是因为React 18的并发模式吗?当我们从React 17迁移到React 18时,由于这种行为的改变,我们需要做什么调整?

Codesandbox:

推荐答案

React 18对setState进行了更改,以便多个渲染可以被批处理到单个渲染中.React 17也有NTFS,但它只能适用于在react生命周期事件(例如useEffect)或dom事件(例如onClick)中发生的同步代码.React 18扩展了这个功能,所以现在它可以在你的rectuc函数中工作.

因此,由示例引起的渲染不再同步发生.相反,react等待,看看剩余的同步代码是否会设置任何其他状态.

https://react.dev/blog/2022/03/29/react-v18#new-feature-automatic-batching

当我们从React 17迁移到React 18时,由于这种行为的改变,我们需要做什么调整?

通常不需要更改;它只是通过将两个渲染合并为一个来提高性能(您的特定示例不会合并任何渲染).如果你使用的是类组件,有一些边缘会 destruct 你的代码.详情请参阅此帖子:https://github.com/reactwg/react-18/discussions/21#discussion-3385721

如果你想强制渲染同步发生,你可以这样做,但很少需要,我不推荐这样做:

import { flushSync } from 'react-dom';
// ...
console.log('before set state');
flushSync(() => {
  setState(val => val + 1);
})
console.log('after set state');

Javascript相关问答推荐

当promise 在拒绝处理程序被锁定之前被拒绝时,为什么我们会得到未捕获的错误?

如何分配类型脚本中具有不同/额外参数的函数类型

Phaser框架-将子对象附加到Actor

如何用显示网格平滑地将元素从一个地方移动到另一个地方?

jQuery提交按钮重新加载页面,即使在WordPress中使用preventDefault()

Phaser 3 console. log()特定游戏角色的瓷砖属性

Angular 17—每当一个布尔变量变为真时触发循环轮询,只要它保持为真

如何让npx在windows中运行js脚本?

Ember.js 5.4更新会话存储时如何更新组件变量

为什么123[';toString';].long返回1?

Nextjs 13.4 Next-Auth 4.2登录(&Quot;凭据&,{});不工作

在FAQ Accodion应用程序中使用React useState时出现问题

Reaction-SWR-无更新组件

如果一个字符串前面有点、空格或无字符串,后面有空格、连字符或无字符串,则匹配正则表达式

未捕获的运行时错误:调度程序为空

try 将Redux工具包与MUI ToggleButtonGroup组件一起使用时出错

如何在和IF语句中使用||和&;&;?

不允许在对象文本中注释掉的属性

如何将值从后端传递到前端

如何按区域进行过滤并将其从结果数组中删除?