React 批量更新

一些组件也许想要更新状态来响应同一事件。下面这个例子是假设的,但是却说明了一个常见的模式:

function Parent() {
  let [count, setCount] = useState(0);
  return (
    <div onClick={() => setCount(count + 1)}>
      Parent clicked {count} times
      <Child />
    </div>
  );
}

function Child() {
  let [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(count + 1)}>
      Child clicked {count} times
    </button>
  );
}

当事件被触发时,子组件的 onClick 首先被触发(同时触发了它的 setState )。然后父组件在它自己的 onClick 中调用 setState 。

如果 React 立即重渲染组件以响应 setState 调用,最终我们会重渲染子组件两次:

*** 进入 React 浏览器 click 事件处理过程 ***
Child (onClick)
  - setState
  - re-render Child // 😞 不必要的重渲染
Parent (onClick)
  - setState
  - re-render Parent
  - re-render Child
*** 结束 React 浏览器 click 事件处理过程 ***

第一次 Child 组件渲染是浪费的。并且我们也不会让 React 跳过 Child 的第二次渲染因为 Parent 可能会传递不同的数据由于其自身的状态更新。

这就是为什么 React 会在组件内所有事件触发完成后再进行批量更新的原因:

*** 进入 React 浏览器 click 事件处理过程 ***
Child (onClick)
  - setState
Parent (onClick)
  - setState
*** Processing state updates                     ***
  - re-render Parent
  - re-render Child
*** 结束 React 浏览器 click 事件处理过程  ***

组件内调用 setState 并不会立即执行重渲染。相反,React 会先触发所有的事件处理器,然后再触发一次重渲染以进行所谓的批量更新。

批量更新虽然有用但可能会让你感到惊讶如果你的代码这样写:

  const [count, setCounter] = useState(0);

  function increment() {
    setCounter(count + 1);
  }

  function handleClick() {
    increment();
    increment();
    increment();
  }

如果我们将 count 初始值设为 0 ,上面的代码只会代表三次 setCount(1) 调用。为了解决这个问题,我们给 setState 提供了一个 “updater” 函数作为参数:

  const [count, setCounter] = useState(0);

  function increment() {
    setCounter(c => c + 1);
  }

  function handleClick() {
    increment();
    increment();
    increment();
  }

React 会将 updater 函数放入队列中,并在之后按顺序执行它们,最终 count 会被设置成 3 并作为一次重渲染的结果。

当状态逻辑变得更加复杂而不仅仅只是少数的 setState 调用时,我建议你使用 useReducer Hook 来描述你的局部状态。它就像 “updater” 的升级模式在这里你可以给每一次更新命名:

  const [counter, dispatch] = useReducer((state, action) => {
    if (action === 'increment') {
      return state + 1;
    } else {
      return state;
    }
  }, 0);

  function handleClick() {
    dispatch('increment');
    dispatch('increment');
    dispatch('increment');
  }

action 字段可以是任意值,尽管对象是常用的选择。

原文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值