不要再滥用useEffect了

213 阅读3分钟

特性

  • useEffect是在渲染完成后触发
  • useEffect是异步触发

引起的问题

多次渲染

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

const [other, setOther] = useState(0);

const addCount = () => {
    setCount(prev => prev + 1);
}

useEffect(() => {
    setOther(count + 1);
}, [count]);

当执行addCount时,组件触发两次渲染

频繁触发的场景

此类问题需要模拟一个购物场景来进行说明:

现有商品列表组件、优惠券组件
优惠券组件中useEffect用于监听勾选商品时,默认勾选最大可用优惠券

当对商品频繁进行勾选、取消操作时,因为useEffect异步触发的机制
每次改变商品列表的操作,都会触发后续的关联操作

在例如此类的场景下,若相应的逻辑过多,又有多次渲染的问题,当机器的性能较差
就会出现比较严重的操作与展示不同步问题

一些适用or不适用的场景?

事件监听

在一些八杆子打不着的组件间,有使用事件系统进行通信的操作
useEffect在官网上也给出了事件监听的例子
然后,理所当然的就这么使用了

如果在较深层组件中使用事件进行通信,会降低代码的可读性
如果再进一步的滥用事件通信,那对项目的维护,就会造成巨大的影响

个人的理解是,能不用事件就尽量不用,如果非得用,也需要尽量的放到组件的外层

代替class组件的componentDidUpdate生命周期

function App({count}) {
  const [copy, setCopy] = useState(count + 1);

  useEffect(() => {
    setCopy(count + 1);
  }, [count]);

  //....
  return null;
}

这应该也是种常见的用法,根据props来初始化组件自身的某些state

函数式组件其实已经在弱化生命周期的概念
如果组件内部的state逻辑,需要依赖外部的传参,可以考虑使用props+state的方式
不一定非要把所有的逻辑都放到state上,再去处理props变化的问题
或者reduxuseContext该用的用起来

为组件设置status并监听

enum EStatus {
    loading,
    error,
    normal
}


const [status, setStatus] = useState(EStatus.loading);

useEffect(() => {
    if (EStatus.loading === status) {
      fetch();
    }

    if (EStatus.error === status) {
      // 上传个埋点
    }
}, [status]);

这是我之前使用较多的一种场景,原本的想法是把组件的不同状态封装起来

随着项目的维护,这个逻辑也开始被滥用了:
先改这里,再改那里,最后把status改为loading

这里的逻辑看似与其他逻辑解耦
但是大部分场景,引起状态变化的地方都是要处理相应的逻辑
其他地方处理完了,这里再处理一次,总归是有点多余

因为这里的逻辑固定了,当需要扩展时,也需要相应的修改
最后要改的代码反而变成1+1>2

总结

  • useEffect从设计上来说,功能是非常强大的
  • 可能也是考虑到要对class组件的兼容
  • 但很多时候,并不是能做到这么些事情,就要这么去做

假如一开始,就没有useEffect
那么说不定,我的代码运行的会更快

注:由于技术能力有限,以上内容仅为个人经验总结,并相关可考