最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 新版react context实践总结

    正文概述 掘金(度123)   2021-04-08   748

    react context的api解决的问题是祖先元素与子孙元素的通信问题,在日常的实践中,用到context的地方还是很多的,在新版的context出来之前旧版的context存在着一定的缺陷,主要是其实现方式是祖先组件一层一层向下传递的过程,只是不用开发者写传递的代码而已,react会自动传递,示意图如下

    新版react context实践总结

    因为一层一层传递的设计,先不说性能问题,出现三层或者三层以上的结构,当中间组件出现shouldComponentUpdate(pureComponent)这样的优化时,中间组件返回了false,没有render,那么子组件或者后代组件是拿不到新的context的。

    新版的context则是无视像shouldComponentUpdate,purecomponent,react.memo这些对于state或者props进行比较从而不render的这些优化的,并且传递的方式发生了根本性的变化

    新版react context实践总结

    但是在日常开发中,错误、不正确或者不是最佳的写法,往往导致代码的性能没有尽可能最大化的发挥新版context的优势,so,在此总结几种常见的错误,以及最好的写法实践

    首先来看一下一种常见的错误

    value属性总是被赋值为新的对象

    错误写法

    import React, { useState, useContext } from 'react';
    
    const TestContext = React.createContext();
    
    function Children(){
        const context = useContext(TestContext);
        console.log('render children', context);
        return 'children';
    }
    
    function Body(){
        console.log('body render');
        return (
            <div>
                body
                <Children />
            </div>
        );
    }
    
    function Header(){
        console.log('header render');
        return 'header';
    }
    
    function Home() {
        const [content, setContent] = useState('yellow');    const [homeRender, setHomeRender] = useState(1);
        const contextValue = useMemo(() => ({ them: content }), [content]);
        return (
            <div>
                <TestContext.Provider value={{ them: content }}>
                    <button onClick={() => setHomeRender(homeRender + 1)}>home render</button>
                    <button onClick={() => { setContent('block') ; }}>set context</button>
                home
                    <Header />
                    <Body />
                </ TestContext.Provider >
            </div>
        );
    }
    

    执行homerender

    新版react context实践总结

    执行setContent

    新版react context实践总结

    这种错误对于熟悉react的小伙伴应该能看出问题所在

       <TestContext.Provider value={{ them: content }}>
    

    这样的写法传递value,在home每一次render的情况下都会产生一个新的对象,传递给context,从而导致接收context的组件从新渲染

    但是从渲染的结果来看好像没什么问题,children也只渲染了一次

    但是我们换一种写法,此时我们想对于children进行优化,我们使用react.memo包一下(这里只展示更改的代码)

    const ChildrenMemo = React.memo(Children);
    function Body(){
        console.log('body render');
        return (
            <div>
                body
                <ChildrenMemo />
            </div>
        );
    }
    

    正常情况下来讲,children并没有接收props,所以当父组件渲染时,children是不会触发render的,但是当我们执行homerender的时候

    新版react context实践总结

    children也跟着渲染了,因为新版api无视react.memo的特性,又因为写法问题(每一次渲染都产生一个新的对象传递给context),导致react.memo失效

    优化写法

    我们可以使用hook的api,usememo很方便的对于value进行缓存优化

       const contextValue = useMemo(() => ({ them: content }), [content]);
       <TestContext.Provider value={contextValue}>
    

    再次测试:

    homeRender

    新版react context实践总结

    setContext

    新版react context实践总结

    可以看到home render的时候因为react.memo的优化并没有触发children render,而只有context更新的时候children才执行render

    但是还记得之前我们说的,新版最大的优势是跳过中间组件,直接传递context么,那么理想的状态不应该是祖先组件context更新,孙组件接收context更新,中间父组件由于没有接收context,而不进行render

    跳过中间组件的context

    我们直接上代码

    import React, { useState, useContext, useMemo } from 'react';
    
    const TestContext = React.createContext();
    
    function Children(){
        const context = useContext(TestContext);
        console.log('render children', context);
        return 'children';
    }
    const ChildrenMemo = React.memo(Children);
    function Body(){
        console.log('body render');
        return (
            <div>
                body
                <ChildrenMemo />
            </div>
        );
    }
    
    
    function Header(){
        console.log('header render');
        return 'header';
    }
    
    
    function TestProvider({ children }) {
        const [content, setContent] = useState('yellow');
        const contextValue = useMemo(() => ({ them: content }), [content]);
        return (
            <>
                <TestContext.Provider value={contextValue}>
                    {
                        children
                    }
                    <button onClick={() => { setContent('block') ; }}>set context</button>
                </ TestContext.Provider >
    
    
            </>
        );
    }
    
    
    function Home() {
        const [homeRender, setHomeRender] = useState(1);
        console.log('home render');
        return (
            <div>
                <TestProvider>
                    <button onClick={() => setHomeRender(homeRender + 1)}>home render</button>
                        home
                    <Header />
                    <Body />
                </TestProvider>
            </div>
        );
    }
    

    我们抽离了context相关的state到一个单独的provider组件中,并使用放在了home中,包裹了header组件和body组件

    为了方便测试我们在home中执行了一次log

    homerender

    新版react context实践总结

    children组件并没有重现render,因为我们使用react.memo包裹了children组件

    setContext

    新版react context实践总结

    只有children组件重现渲染,完美

    我们分析一下各个组件不渲染和渲染的原因

    home组件: provider为home的子组件,子组件state更新不会影响到父组件

    header/body: 这两个组件作为props的children属性传递到provider中,并不算provider的子组件,所以并没有render

    children: context的更新触发了children的重新render

    so,日常我们在开发过程中,注意context provider组件的抽离会对整体的性能产生不小的提升

    避免滥用context

    最后一点,也是最终的一点是,应该避免滥用context,例如上述的示例代码就是滥用context,context传递的应该是一个不常变的数据,在react-redux6版本就是因为采用的context来直接传递state而导致一些性能下降的问题,react团队也提到了相关问题,他们是不建议使用context传递多变数据的,所以更加合理的组件设计才是性能提升的最基本也是最有效的解决办法.


    起源地下载网 » 新版react context实践总结

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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