深入剖析useMemo与useEffect,到底该怎么用?

310 阅读3分钟

我正在参加「掘金·启航计划」

WechatIMG111.jpeg

前言

疑惑了很久,以为useMemo和useEffect 作用是一样的,因为书写形式和调用机制都十分的像,都是传入一个函数,都在关联变量发生变化时才去执行函数。其实这两者的区别十分的大,下面我将深入剖析这两者的不同,希望对大家有所帮助同时欢迎讨论指出问题!

 const [countA, setCountA] = useState(1)
 const [countB, setCountB] = useState(1)

  function todo() {
    console.log('todo触发')
    return countA
  }

  return (
    <div className="App">
      <p>{todo()}</p>
      <p>A:{countA}</p>
      <p>B:{countB}</p>
      <Button onClick={() => setCountA(countA + 1)}>点击A</Button>
      <Button onClick={() => setCountB(countB + 1)}>点击B</Button>
    </div>
  )

分析需求: 在每次setState后,都会触发Dom重新渲染,会触发函数,但是现在我们只想让这个函数只在A改变的时候再触发,而不是每次重新渲染都触发。

此时是:点击A,点击B 都会触发todo函数

2.使用useEffect解决

此时我的第一反应就是使用useEffect来解决,将上面的函数用useEffect包裹,并设置依赖,只有name发生变化的时候才触发。

  const [countA, setCountA] = useState(1)
  const [countB, setCountB] = useState(1)

  function todo(s?: any) {
    console.log('todo触发' + s)
    return countA
  }

  useEffect(() => {
    console.log('countA')
    todo('A')
  }, [countA])

  useEffect(() => {
    console.log('countB')
  }, [countB])

  return (
    <div className="App">
      <p>A:{countA}</p>
      <p>B:{countB}</p>
      <Button onClick={() => setCountA(countA + 1)}>点击A</Button>
      <Button onClick={() => setCountB(countB + 1)}>点击B</Button>
    </div>
  )
  
  点击A打印结果是:
  1. countA
  2.todo触发A

从结果即可看出:useEffect是在Dom渲染之后,再执行函数。类似于componentDidMount,componentDidUpdate,componentWillUnmount三个函数的组合 但是这种方式有一个缺点,只是在渲染前控制,而如果有业务要求重新渲染控制需要在渲染期间控制,也就是在return中进行控制。useEffect是不能实现的。

2.使用useMemo解决

 const [countA, setCountA] = useState(1)
 const [countB, setCountB] = useState(1)

  function todo(s?: any) {
    console.log('todo触发' + s)
    return countA
  }

  const memo = useMemo(() => {
    console.log('memo 触发')
    return () => countA
  }, [countA])

  return (
    <div className="App">
      <p>{memo()}</p>
      <p>A:{countA}</p>
      <p>B:{countB}</p>
      <Button onClick={() => setCountA(countA + 1)}>点击A</Button>
      <Button onClick={() => setCountB(countB + 1)}>点击B</Button>
    </div>
  )
   
  点击A打印结果是:
  1.memo 触发
  2.todo触发
  

从结果即可看出:useMemo是在渲染期间执行函数,类比生命周期就是shouldComponentUpdate,因此useMemo就能解决一些问题,怎么在Dom改变的时候,控制某些函数不被触发。

1.假设在上面函数组件里面有一个定时任务,选用哪种方式来解决呢?是useEffect,还是useMemo呢?

function todo(s?: any) {
    setInterval(() => {
         console.log('触发了定时器')
       }, 5000)
    return countA
  }
  
  多次点击A打印结果是:
  1.countA
  2.todo触发了定时器A
  可以发现 ‘todo触发了定时器A’ 在多次点击之后 输出的频率再增多。
  原因是:每执行一次,又会添加一个定时器,这时就容易产生内存泄漏

3.总结

区别:1.useEffect会在组件渲染完毕后执行,和渲染无关的副作用。useMemo:会在在组件首次加载和重渲染期间执行,执行的函数需要和渲染相关的。 2.useEffect 没有返回值,useMemo有返回值 3.useMemo中不能做操作Dom之类的操作,这是属于 useEffect 的适用范畴

结束语

希望大家能够喜欢我的文章,我真的很用心在写,也希望通过文章认识更多志同道合的朋友。

最后伙伴们,如果喜欢我的可以给点一个小小的赞👍或者关注➕都是对我最大的支持。