React hooks错误用法

1. 不是所有的依赖都必须放到依赖数组中

对于所有的 React Hooks 用户,都有一个共识:“useEffect 中使用到外部变量,都应该放到第二个数组参数中”,同时我们会安装 eslint-plugin-react-hooks 插件,来提醒自己是不是忘了某些变量。

以上共识来自官方文档:
https://zh-hans.reactjs.org/docs/hooks-reference.html#useeffect
在这里插入图片描述

我愿称该条规则为万恶之源,这条规则以高亮展示,所有的新人都很重视,包括我自己。然而在实际的开发中,发现事情并不是这样的。

下面举一个比较简单的例子,要求如下:当 props.countcount 变化时,上报当前所有数据。

这个例子比较简单,先贴下源码:


function Demo(props) {
   
  const [count, setCount] = useState(0);
  const [text, setText] = useState('');
  const [a, setA] = useState('');

  useEffect(() => {
   
    monitor(props.count, count, text, a);
  }, [props.count, count]);

  return (
    <div>
      <button
        onClick={
   () => setCount(c => c + 1)}
      >
        click
      </button>
      <input value={
   text} onChange={
   e => setText(e.target.value)} />
      <input value={
   a} onChange={
   e => setA(e.target.value)} />
    </div>
  )
}
复制代码

我们能看到示例代码中,useEffect 是不符合 React 官方建议的,texta 变量没有放到依赖数组中,ESLint 警告如下:

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/fe679228e09a476cb9040b66f794a3c2~tplv-k3u1fbpfcp-watermark.awebp

那如果按照规范,我们把依赖项都放到第二个数组参数中,会怎样呢?

  useEffect(() => {
   
    monitor(props.count, count, text, a);
  }, [props.count, count, text, a]);
复制代码

如上的代码虽然符合了 React 官方的规范,但不满足我们的业务需求了,当 texta 变化时,也触发了函数执行。

此时陷入了困境,当满足 useEffect 使用规范时,业务需求就不能满足了。当满足业务需求时,useEffect 就不规范了。

我的建议为:

  1. 不要使用 eslint-plugin-react-hooks 插件,或者可以选择性忽略该插件的警告。
  2. 只有一种情况,需要把变量放到 deps 数组中,那就是当该变量变化时,需要触发 useEffect 函数执行。而不是因为 useEffect 中用到了这个变量!

2. deps 参数不能缓解闭包问题

假如完全按第二个建议来写代码,很多人又担心,会不会造成一些不必要的闭包问题?我的结论是:闭包问题和 useEffect 的 deps 参数没有太大关系。

比如我有一个这样的需求:当进入页面 3s 后,输出当前最新的 count。代码如下:

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

  useEffect(() => {
   
    const timer = setTimeout(() => {
   
      console.log(count)
    }, 3000);
    return () => {
   
      clearTimeout(timer);
    }
  }, [])

  return (
    <button
      onClick={
   () => setCount(c => c + 1)}
    >
      click
    </button>
  )
}
复制代码

以上代码,实现了初始化 3s 后,输出 count。但很遗憾,这里肯定会出闭包问题,哪怕进来之后我们多次点击了 button,输出的 count 仍然为 0。

那假如我们把 count 放到 deps 中,是不是就好了?

  useEffect(() => {
   
    const timer = setTimeout(() => {
   
      console.log(count)
    }, 3000);
    return () => {
   
      clearTimeout(timer);
    }
  }, [count])
复制代码

如上代码,此时确实没有闭包问题了,但在每次 count 变化时,定时器卸载并重新开始计时了,不满足我们的最初需求了。

要解决的唯一办法为:


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值