封装一个useSyncEffect减少不必要的渲染

63 阅读1分钟

场景

在react中,有的时候我们会在useEffect中去执行set操作,如下

function A() {
  console.log("A render")
  const [,update] = useState()
  useEffect(() => {
       update()
  }, [])
  return <B/>
}
function B() {
  console.log("B render")
}

由于useEffect内的函数是异步调用的,在第一次渲染完成后开始第二次渲染,以上代码输出

A render
B render
A render
B render

可以看到子组件渲染了两次,其实是没有必要的,我们可以封装一个同步调用的useEffect

function useSyncEffect(fn, deps) {
  const dp = useRef([])
  function run() {
    dp.current = deps
    fn()
  }
  if (dp.current.length !== deps.length) {
    run()
  } else {
    for (let i = 0; i < deps.length; i++) {
      if (deps[i] !== dp.current[i]) {
        run()
        break
      }
    }
  }
}

将以上代码改为

function A() {
  console.log("A render")
  const [,update] = useState()
  useSyncEffect(() => {
       update()
  }, [])
  return <B/>
}
function B() {
  console.log("B render")
}

输出结果

A render
A render
B render

缺陷

因为这个方法是同步调用的,用useRef所以绑定dom的话,ref.current是undefined,这个时候还是应该用useEffect

function A() {
  console.log("A render")
  const ref = useRef()
  useSyncEffect(() => {
       console.log(ref.current)//undefined
  }, [])
  return <div ref={ref}></div>
}