深入浅出useEffect

1.在组件渲染后执行后运行,并且不会阻塞浏览器更新 UI。(异步)

 

2. 依赖数组控制执行时机

useEffect 接受两个参数:

useEffect(() => { // 副作用逻辑 }, [dependency]);

无依赖数组([] 省略):每次组件渲染后都会执行。

空依赖数组([]):仅在组件挂载(mount)时执行一次,不会在更新时执行。

依赖数组包含变量([dependency]):仅当依赖项发生变化时才会重新执行。

 

3.返回清理函数(Cleanup)

useEffect 可以返回一个函数,在组件卸载(unmount)或依赖项更新前执行清理操作。

useEffect 返回的回调函数,会在 useEffect 重新执行之前,执行一次;并且拿不到最新的值

 
     // render
      // |
      // V
      //   浏览器绘制
      // |
      // V
      // 清理上一次的 effects(就是执行 useEffect 的返回值函数)
      // |
      // V
      // 运行本次的 effects

      // 首先执行组件的渲染(render)
      // 然后浏览器完成绘制
      // 接着执行清理工作(执行上一次effect的清理函数)
      // 最后运行新的effects

      // 父组件的render总是先执行
      // 子组件的useEffect会在父组件的useEffect之前执行
      // 当依赖项改变时,会先执行清理函数(return的函数),然后再执行新的effect
      // 组件卸载时会执行相应的清理函数

 

      function App() {
        console.log('app render')
        console.log('before useEffect')
        const [visible, setVisible] = React.useState(true)

        React.useEffect(() => {
          console.log('app useEffect visible', visible)
          return () => {
            console.log('app useEffect return visible', visible)
          }
        }, [visible])

        React.useEffect(() => {
          console.log('app useEffect []', visible)
          return () => {
            console.log('app useEffect return []', visible)
          }
        }, [])

        React.useEffect(() => {
          console.log('app useEffect 空值', visible)
          return () => {
            console.log('app useEffect return 空值', visible)
          }
        })

        const handleClick = () => {
          setVisible((visible) => {
            return !visible
          })
        }

        const Child = () => {
          React.useEffect(() => {
            console.log('child useEffect')
            return () => {
              console.log('child useEffect return')
            }
          }, [])
          console.log('child render')
          return <div>I' m child</div>
        }

        // 为同步
        const getCount = () => {
          console.log('getCount', visible)
          return visible
        }

        React.useEffect(() => {
          console.log('useEffect', visible)
        }, [getCount()])
        // 等价于 }, [visible])

        const getNaN = () => {
          console.log('getNaN')
          return NaN
        }

        React.useEffect(() => {
          console.log('useEffect', visible)
        }, [getNaN()])

        React.useEffect(() => {
          console.log('每次都执行')
        })

        React.useEffect(() => {
          console.log('只在第一次执行')
        }, [])

        console.log('after useEffect')

        return (
          <div>
            <button onClick={handleClick}>Click me toggle child1</button>
            {visible ? <Child /> : null}
          </div>
        )
      }

1.初始化输出:

 2.点击handleClick,visible为false

 3.点击handleClick,visible为true

 4.简单实现useEffect

      const preDepsCollect = []
      let effectIndex = 0

      function useMyEffect(callback, curDeps) {
        if (Object.prototype.toString.call(callback) !== '[object Function]') {
          throw new Error('The first argument must be a function')
        }

        if (typeof curDeps === 'undefined') {
          setTimeout(callback)
        } else {
          if (Object.prototype.toString.call(curDeps) !== '[object Array]') {
            throw new Error('The second argument must be an array')
          } else {
            const preDeps = preDepsCollect[effectIndex]
            const hasChanged = preDeps
              ? curDeps.every((dep, index) =>
                  Object.is(dep, preDeps[index])
                ) === false
              : true
            if (hasChanged) {
              setTimeout(callback)
            }
            preDepsCollect[effectIndex] = curDeps
            effectIndex++
          }
        }
      }


      function App() {
        // 重制下标
        effectIndex = 0

        useMyEffect(() => {
          console.log('每次都执行')
        })

        useMyEffect(() => {
          console.log('只在第一次执行')
        }, [])



        return (
          <div></div>
        )
      }

 

posted on 2025-04-03 14:43  sss大辉  阅读(73)  评论(0)    收藏  举报

导航