React useMemo useCallback useEffect 的区别(保姆级教程)

因个人工作原因,在2023年学起了React + TS 这个 "前端大佬 " "高阶玩家 " 标配的技术栈,一套学习下来个人总结就是:React真特么难用!传染病式的渲染逻辑是真让人难受!维护之前的代码就是深渊!难怪React项目标配TS,没个TS给这货加点限制这玩意写出来的代码就更没法看了!------以上总结仅代表个人使用感受,至于React的设计如何牛逼,我感受不到,我也不配讨论,勿喷!

如何解决React组件抽风式的刷新问题,小编使用解释和源码示例的方式讲解一下

memo

  • 父组件属性变化都会导致子组件重新执行,即使传入子组件props没有任何变化,甚至子组件没有依赖于任何props属性,都会导致子组件重新渲染
  • 使用memo包裹子组件时,只有props发生改变子组件才会重新渲染,以提升一定的性能
TypeScript 复制代码
// 子组件
// 若不适用 memo API,父组件每次跟新 time 时,子组件都会重新渲染,输出 'List 被渲染'
const List = memo((props:{
  list:Array<string>
})=>{
  console.log('List 被渲染')
  return (
    <>
      <ol>
      {
        props?.list.map(item=>{
          return ( <li key={item}>{item}</li> )
        })
      }
      </ol>
    </>
  )
})

export default () => {
  const [ time,setTime ] = useState(0);
  const [ list,setList ] = useState<string[]>([]);

  useEffect(()=>{
    setTimeout(() => {
      setTime(time+1);
    }, 1000);
  },[time])

  return (
    <>
      <h1>页面浏览时长:{time}秒</h1>
      <List list={list} />
    </>
  );
};

useMemo

  • 父组件将一个值传递给子组件,若父组件的其他值发生变化时,子组件也会跟着渲染多次,会造成性能浪费; useMemo是将父组件传递给子组件的值缓存起来,只有当 useMemo中的第二个参数状态变化时,子组件才重新渲染
  • useMemo便是用于缓存该函数的执行结果,仅当依赖项改变后才会重新计算
TypeScript 复制代码
// 子组件
const List = memo((props:{
  oddOrEven?:string,
  list:Array<string>
})=>{
  console.log('List 被渲染')
  return (
    <>
      <h3>列表长度为:{props.oddOrEven}</h3>
      <ol>
      {
        props?.list.map(item=>{
          return ( <li key={item}>{item}</li> )
        })
      }
      </ol>
    </>
  )
})

export default () => {  
  const [ time,setTime ] = useState(0);
  const [ list,setList ] = useState<string[]>([]);

  useEffect(()=>{
    setTimeout(() => {
      setTime(time+1);
    }, 1000);
  },[time])


  // 奇数或偶数
  // 当传递给子组件的值是通过函数返回的情况下,需要使用 useMemo Hook
  const oddOrEven = useMemo(()=>{
    return (list.length % 2 === 0 ? '偶数':'奇数');
  },[list])


  return (
    <>
      <h1>页面浏览时长:{time}秒</h1>
      <List list={list} oddOrEven={oddOrEven} />
    </>
  );
};

useCallback

  • 父组件将一个方法传递给子组件,若父组件的其他状态发生变化时,子组件也会跟着渲染多次,会造成性能浪费; usecallback是将父组件传给子组件的方法给缓存下来,只有当 usecallback中的第二个参数状态变化时,子组件才重新渲染
  • 如果传入的props包含函数,父组件每次重新渲染都是创建新的函数,所以传递函数子组件还是会重新渲染,即使函数的内容还是一样,我们希望把函数也缓存起来,于是引入useCallback
TypeScript 复制代码
// 子组件
const List = memo((props:{
  list:Array<number>,
  click:(number:any)=>void
})=>{
  console.log('List 被渲染')
  return (
    <>
      <button type='button' onClick={()=>{
        const t = new Date().getTime();
        props.click(t);
      }}>添加</button>
      <ol>
      {
        props?.list.map(item=>{
          return ( <li key={item}>{item}</li> )
        })
      }
      </ol>
    </>
  )
})

export default () => {
  const [ time,setTime ] = useState(0);
  const [ list,setList ] = useState<number[]>([]);

  useEffect(()=>{
    setTimeout(() => {
      setTime(time+1);
    }, 1000);
  },[time])
    
  // 若传递给子组件的属性有函数,需要使用 useCallback Hook,否则子组件发疯式的重新渲染
  const handleClick = useCallback((time:number)=>{
    setList([
      ...list,
      time
    ])
  },[list])

  return (
    <>
      <h1>页面浏览时长:{time}秒</h1>
      <List list={list} click={ handleClick } />
    </>
  );
};

useMemo 和 useEffect 区别

  • useEffect是在DOM改变之后触发,useMemo在DOM渲染之前触发
  • useEffect可以帮助我们在DOM更新完成后执行某些副作用操作,如数据获取,setState
  • 不要在这个useMemo函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo
  • 在useMemo中使用setState你会发现会产生死循环,并且会有警告,因为useMemo是在渲染中进行的,你在其中操作DOM后,又会导致触发memo

小分享:我每次看完React文档后,我觉得我学会了,过一周后我发现自己还没学会。。这玩意坑到底还有多少,我醉了!

作者:++黄河爱浪++

本文原创,著作权归作者所有,转载请注明原链接及出处

相关推荐
Setsuna_F_Seiei2 小时前
前端转型 Agent 开发 05 之 Agent Hooks 与 Checkpointer(让 Agent 从全自动转变人为可掌控)
前端·agent·ai编程
百万蹄蹄向前冲4 小时前
风扇转了一晚上MVP专家团翻车事故
前端·人工智能
默_笙4 小时前
🏛 给 AI 配一间办公室:Harness Engineering 六大模块与它的实现
前端·javascript
linux_cfan5 小时前
videojs v10 源代码系列解读:14 · 谓词守卫:在运行时安全地调用能力
前端·javascript·音视频
狗狗狗狗狗乐啊5 小时前
搭一个 AI 对话工作台 AChat:从 0 到可用的完整记录(一)
python·react.js·ai编程
kyriewen6 小时前
我扒了 10,221 条 JD:腾讯技术岗 75% 在要 AI
前端·人工智能·ai编程
郑州光合科技余经理6 小时前
同城外卖小程序开发:下单成功后,后台导出能不能对上用户端状态
开发语言·前端·git·后端·uni-app·php·ai编程
IT_陈寒7 小时前
Vue的响应式让我熬到凌晨三点,原来漏了这个小细节
前端·人工智能·后端
Blanche15007 小时前
利用 RAG 为答疑机器人扩展知识范围
前端
天若有情6737 小时前
【纯前端小工具】公历生日转农历,批量查询每年农历生日对应的公历日期(GitHub Pages在线直接用)
前端·javascript·github pages·农历转换·lunisolar·网页小工具