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


    因个人工作原因,在2023年学起了React + TS 这个 “前端大佬” “高阶玩家” 标配的技术栈,一套学习下来个人总结就是:React真特么难用!传染病式的渲染逻辑是真让人难受!

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

    memo

    • 父组件属性变化都会导致子组件重新执行,即使传入子组件props没有任何变化,甚至子组件没有依赖于任何props属性,都会导致子组件重新渲染
    •  使用memo包裹子组件时,只有props发生改变子组件才会重新渲染,以提升一定的性能
    1. // 子组件
    2. // 若不适用 memo API,父组件每次跟新 time 时,子组件都会重新渲染,输出 'List 被渲染'
    3. const List = memo((props:{
    4. list:Array<string>
    5. })=>{
    6. console.log('List 被渲染')
    7. return (
    8. <>
    9. <ol>
    10. {
    11. props?.list.map(item=>{
    12. return ( <li key={item}>{item}li> )
    13. })
    14. }
    15. ol>
    16. )
    17. })
    18. export default () => {
    19. const [ time,setTime ] = useState(0);
    20. const [ list,setList ] = useState<string[]>([]);
    21. useEffect(()=>{
    22. setTimeout(() => {
    23. setTime(time+1);
    24. }, 1000);
    25. },[time])
    26. return (
    27. <>
    28. <h1>页面浏览时长:{time}秒h1>
    29. <List list={list} />
    30. );
    31. };

    useMemo

    • 父组件将一个值传递给子组件,若父组件的其他值发生变化时,子组件也会跟着渲染多次,会造成性能浪费; useMemo是将父组件传递给子组件的值缓存起来,只有当 useMemo中的第二个参数状态变化时,子组件才重新渲染
    • useMemo便是用于缓存该函数的执行结果,仅当依赖项改变后才会重新计算
    1. // 子组件
    2. const List = memo((props:{
    3. oddOrEven?:string,
    4. list:Array<string>
    5. })=>{
    6. console.log('List 被渲染')
    7. return (
    8. <>
    9. <h3>列表长度为:{props.oddOrEven}h3>
    10. <ol>
    11. {
    12. props?.list.map(item=>{
    13. return ( <li key={item}>{item}li> )
    14. })
    15. }
    16. ol>
    17. )
    18. })
    19. export default () => {
    20. const [ time,setTime ] = useState(0);
    21. const [ list,setList ] = useState<string[]>([]);
    22. useEffect(()=>{
    23. setTimeout(() => {
    24. setTime(time+1);
    25. }, 1000);
    26. },[time])
    27. // 奇数或偶数
    28. // 当传递给子组件的值是通过函数返回的情况下,需要使用 useMemo Hook
    29. const oddOrEven = useMemo(()=>{
    30. return (list.length % 2 === 0 ? '偶数':'奇数');
    31. },[list])
    32. return (
    33. <>
    34. <h1>页面浏览时长:{time}秒h1>
    35. <List list={list} oddOrEven={oddOrEven} />
    36. );
    37. };

    useCallback

    • 父组件将一个方法传递给子组件,若父组件的其他状态发生变化时,子组件也会跟着渲染多次,会造成性能浪费; usecallback是将父组件传给子组件的方法给缓存下来,只有当 usecallback中的第二个参数状态变化时,子组件才重新渲染
    • 如果传入的props包含函数,父组件每次重新渲染都是创建新的函数,所以传递函数子组件还是会重新渲染,即使函数的内容还是一样,我们希望把函数也缓存起来,于是引入useCallback
    1. // 子组件
    2. const List = memo((props:{
    3. list:Array<number>,
    4. click:(number:any)=>void
    5. })=>{
    6. console.log('List 被渲染')
    7. return (
    8. <>
    9. <button type='button' onClick={()=>{
    10. const t = new Date().getTime();
    11. props.click(t);
    12. }}>添加button>
    13. <ol>
    14. {
    15. props?.list.map(item=>{
    16. return ( <li key={item}>{item}li> )
    17. })
    18. }
    19. ol>
    20. )
    21. })
    22. export default () => {
    23. const [ time,setTime ] = useState(0);
    24. const [ list,setList ] = useState<number[]>([]);
    25. useEffect(()=>{
    26. setTimeout(() => {
    27. setTime(time+1);
    28. }, 1000);
    29. },[time])
    30. // 若传递给子组件的属性有函数,需要使用 useCallback Hook,否则子组件发疯式的重新渲染
    31. const handleClick = useCallback((time:number)=>{
    32. setList([
    33. ...list,
    34. time
    35. ])
    36. },[list])
    37. return (
    38. <>
    39. <h1>页面浏览时长:{time}秒h1>
    40. <List list={list} click={ handleClick } />
    41. );
    42. };

    useMemo 和 useEffect 区别

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

    其他

    Q:子组件更新孙组件不更新如何实现?

    答:

    1. 将孙组件CardHead 以 props 的方式传递给 子组件 Card
    2. 组件使用 useMemo 缓存 孙组件的渲染结果,给孙组件 使用 memo 也是一样的效果,总结原因就是找到不被刷新的那个层级,通过 props 传值
    1. // 子组件
    2. const Card = (props:any)=>{
    3. console.log('渲染组件 Card')
    4. return (
    5. <div style={{border:'#ccc solid 1px'}}>
    6. <div>
    7. {props.head}
    8. div>
    9. <div>
    10. <h2>card-bodyh2>
    11. div>
    12. div>
    13. )
    14. }
    15. // 孙组件
    16. const CardHead = ({count}:{count:number})=>{
    17. console.log('渲染组件 Card-Head')
    18. return (
    19. <h1>card-header -- 点击统计:{count}h1>
    20. )
    21. }
    22. export default () => {
    23. const [ time,setTime ] = useState(0);
    24. const [ countClick, setCountClick ] = useState(0);
    25. const cardHeader = useMemo(()=>{
    26. return <CardHead count={countClick} />
    27. },[countClick])
    28. useEffect(()=>{
    29. setTimeout(() => {
    30. setTime(time+1);
    31. }, 1000);
    32. },[time])
    33. return (
    34. <>
    35. <h1>页面浏览时长:{time}秒h1>
    36. <button type='button' onClick={()=>{
    37. setCountClick(countClick+1)
    38. }}>点击统计button>
    39. <Card head={cardHeader} />
    40. );
    41. };

    最后唠叨几句:我每次看完React文档后,我觉得我学会了,过一周后我发现自己还没学会。这玩意坑到底还有多少,我醉了!我在知乎上看到过有个人说“这世界上没有人能真正会用 useEffect”,想深入了解学习可以阅读 《useEffect 完整指南》,看完文章真让人上头。

    我讨厌 React!

    作者:黄河爱浪

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

  • 相关阅读:
    kafka(四)消息类型
    【编程题】【Scratch一级】2022.09 踢足球
    [线程与网络] 网络编程与通信原理(五): 深入理解网络层IP协议与数据链路层以太网协议
    代码随想录算法训练营第二十七天| 131.分割回文串
    tiktok接口对接
    QDir实践
    知识蒸馏 pytorch官网源码分析
    Jenkins实现基础CI操作
    SpinalHDL学习笔记(1)——Scala和sbt安装
    微服务框架 SpringCloud微服务架构 7 Feign 7.1 基于Feign 远程调用
  • 原文地址:https://blog.csdn.net/u013350495/article/details/133955967