我的名字是:Kyrie</h1><h1 class="name">我的名字是:IRVING</...">
最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • useEffect进阶指南(下)

    正文概述 掘金(kyrie11)   2021-03-23   461

    React的内部更新机制?

    <h1 class="name">我的名字是:Kyrie</h1>
    <h1 class="name">我的名字是:IRVING</h1>
    React也跟vue有着Diff算法,它每次render只会更新与上一次渲染不同的节点和内容,所以上面的例子,React只需要这样做:

    h1.innerText = Kyrie -> h1.innerText = IRVING

    那useEffect呢?

    function Demo() {
      const initCount = 0
      const [count, setCount] = useState(initCount)
      
      // 当我们把count作为依赖传入时,count改变就会重新执行
      useEffect(() => {
        console.log('count的值为:', count)
      }, [count])
      
      return (
        <div>
          <h2>{count}</h2>
          <button onClick={() => setCount(count + 1)}>count++</button>
        </div>
      ) 
    }

    所以useEffect的更新机制完成依靠我们传入的依赖,只要在useEffect里使用到的状态值都必须在依赖中声明,让React内部进行依赖更新。

    所以,当依赖的状态变得多起来的时候,难免会让我们在性能方面有所担心。

    useEffect传入依赖的正确方式?

    现在有个需求:书写一个函数,每秒自动让count+1
    function Demo() {
      const initCount = 0
      const [count, setCount] = useState(initCount)
      
      useEffect(() => {
       // 设定时器,每秒执行一次setCount
        const timer = setInterval(() => {
          setCount(count + 1)
        }, 1000)
        
        return () => {
          clearInterval(timer)
        }
      }, [])
      
      return (
        <div>
          <h2>{count}</h2>
          <button onClick={() => setCount(count + 1)}>count++</button>
        </div>
      ) 
    }

    上面的例子,我们把[]作为useEffect的依赖,就是说effect只会在组件挂载后执行一次。定时器只需要设定一个,每秒setInterval后帮我们把count + 1对吧,这不就能实现需求了吗

    看起来貌似没什么问题,但运行起来发现count只自增了一次就停住了,也就是useEffect只执行了一次,what???

    原因很简单,我们把[]作为依赖,React内部就会认为effect函数内没有依赖任何值,所以当useEffect第一次执行时,setCount(0 + 1) 此时count = 1,然后1s后,setCount(1 + 1),count = 2? 不,React内部会自动忽略第二次及以后的每次更新,因为我们把[]作为依赖,就代表了effect只会执行一次,第二次开始,count一直都是1,这就导致了count变为1后就不增加了。

    那好,我们把count传入作为依赖

    useEffect(() => {
       // 设定时器,每秒执行一次setCount
        const timer = setInterval(() => {
          setCount(count + 1)
        }, 1000)
        
        return () => {
          clearInterval(timer)
        }
      }, [count])

    运行之后发现,问题确实解决了,count成功的每秒自动自增,但这其实不是最好的解决方案,因为我们知道每当count值改变,就会触发render,每次都会生成一个新的useEffect,然后执行,重新生成一个定时器,虽然目的达到了,但这显然不是最优解,我们最好能避免每次都生成一个新的定时器,因为这样我们的定时器将毫无意义...

    useEffect(() => {
       // 设定时器,每秒执行一次setCount
        const timer = setInterval(() => {
          setCount(count => count + 1)
        }, 1000)
        
        return () => {
          clearInterval(timer)
        }
      }, [])

    可以看到,我们把count从依赖中取出,然后在setCount(count => count + 1),这样做得目的是为了告诉React,我们只需要count+1,并不需要读取它的值,因为React内部肯定是知道当前count的值,这样effect内部就不依赖count了,useEffect只需执行一次即可,这也是setState的函数写法,函数的参数就是最新的状态值,如果不太了解这种写法的朋友可以去查一下资料,这里就不再过多阐述了...

    useEffect配合useReducer使用?

    这就是useEffect究极使用技巧,用法及其广泛...还是使用上面的例子,让我们用useReducer改写一下

    function Demo() {
      const initState = { count: 0 }
      const [state, dispatch] = useReducer(reducer, initState)
      
      function reducer(state, action) {
        switch(action.type) {
          case 'increment' :
            return {count: state.count + 1}
          default: 
            throw new Error('type不存在...')
        } 
      }
      
      useEffect(() => {
       // 设定时器,每秒执行一次setCount
        const timer = setInterval(() => {
          dispatch({type: 'increment'})
        }, 1000)
        
        return () => {
          clearInterval(timer)
        }
      }, [dispatch]) // 这里我们依赖了dispatch 其实可以省略
      
      return (
        <div>
          <h2>{count}</h2>
          <button onClick={() => setCount(count + 1)}>count++</button>
        </div>
      ) 
    }

    大家可能会问我,这种写法的好处是什么?其实,我们利用useReducer通过action来描述行为,实现状态和行为的分离,在多个依赖的时候这种写法的优势就能很好的体现出来。

    还有最后一个疑点:为什么dispatch可以省略?其实上面的例子,即使我们把dispatch从依赖中取出,也能正常运行,effect也只会执行一次。这么神奇?effect是怎么知道我们的行为是什么?其实,React内部会帮我们记住dispatch的各种行为(action),且能拿到最新的count,这一系列操作是React内部发生的,并不需要放在effect内。

    useMemo和useCallback(性能优化)?

    useCallback(缓存函数)
    const memoizedSetCount = useCallback(
      () => {
        setCount(count + 1)
      },
      [count],
    );

    把内联回调函数及依赖项数组作为参数传入useCallback,它将返回该回调函数的缓存版本,该回调函数仅在某个依赖项改变时才会更新。

    useMemo(缓存值:类似于Vue的计算属性)
    const memoizedCount = useMemo(() => {
      const doubleCount = count * 2
    }, [count]);

    把“创建”函数和依赖项数组作为参数传入useMemo,它仅会在某个依赖项改变时才重新计算缓存值。这种优化有助于避免在每次渲染时都进行高开销的计算。

    到这里,useEffect的进阶指南就介绍完了,要更好的运用React hooks 还得日常开发中的实战和积累,相信在往后的开发中,大家能更合理的使用hooks,也可以封装属于个人的hooks...大家加油,一起学习。

    起源地下载网 » useEffect进阶指南(下)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元