React Effects(副作用)

我们在之前提到过 React 组件在渲染过程中不应该有可观察到的副作用。但是有些时候副作用确实必要的。我们也许需要进行管理 focus 状态、用 canvas 画图、订阅数据源等操作。

在 React 中,这些都可以通过声明 effect 来完成:

function Example() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

如果可能,React 会推迟执行 effect 直到浏览器重新绘制屏幕。这是有好处的因为像订阅数据源这样的代码并不会影响交互时间和首次绘制时间 。

(有一个极少使用的 Hook 能够让你选择退出这种行为并进行一些同步的工作。请尽量避免使用它。)

effect 不只执行一次。当组件第一次展示给用户以及之后的每次更新时它都会被执行。在 effect 中能触及当前的 props 和 state,例如上文例子中的 count 。

effect 可能需要被清理,例如订阅数据源的例子。在订阅之后将其清理,effect 能够返回一个函数:

useEffect(() => {
    DataSource.addSubscription(handleChange);
    return () => DataSource.removeSubscription(handleChange);
  });

React 会在下次调用该 effect 之前执行这个返回的函数,当然是在组件被摧毁之前。

有些时候,在每次渲染中都重新调用 effect 是不符合实际需要的。 你可以告诉 React 如果相应的变量不会改变则跳过此次调用:

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

但是,这往往会成为过早地优化并会造成一些问题如果你不熟悉 JavaScript 中的闭包是如何工作的话。

例如,下面的这段代码是有 bug 的:

  useEffect(() => {
    DataSource.addSubscription(handleChange);
    return () => DataSource.removeSubscription(handleChange);
  }, []);

它含有 bug 因为 [] 代表着“不再重新执行这个 effect 。”但是这个 effect 中的 handleChange 是被定义在外面的。handleChange 也许会引用任何的 props 或 state

  function handleChange() {
    console.log(count);
  }

如果我们不再让这个 effect 重新调用,handleChange 始终会是第一次渲染时的版本,而其中的 count 也永远只会是 0 。

为了解决这个问题,请保证你声明了特定的依赖数组,它包含所有可以改变的东西,即使是函数也不例外:

  useEffect(() => {
    DataSource.addSubscription(handleChange);
    return () => DataSource.removeSubscription(handleChange);
  }, [handleChange]);

取决于你的代码,在每次渲染后 handleChange 都会不同因此你可能仍然会看到不必要的重订阅。 useCallback 能够帮你解决这个问题。或者,你可以直接让它重订阅。例如浏览器中的 addEventListener API 非常快,但为了在组件中避免使用它可能会带来更多的问题而不是其真正的价值。
原文章

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值