最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • React中共享组件逻辑的三种方式

    正文概述 掘金(Null2020)   2020-12-12   568

    废话少说,这三种方式分别是:render props、高阶组件和自定义Hook。下面依次演示

    假设有一个TimeOnPage组件专门用来记录用户在当前页面停留时间,像这样:

    const TimeOnPage = () => {
      const [second, setSecond] = useState(0);
    
      useEffect(() => {
        setTimeout(() => {
          setSecond(second + 1);
        }, 1000);
      }, [second]);
      return (
        <div>停留时间:{second}秒</div>
      );
    }
    

    如果另一个组件需要复用这个功能,我们能否封装一下,以便轻松地与其它组件共享?

    一般很自然地想到子组件嵌套的方式,利用props传参

    const Child = (props) => {
      return <div>stayTime: {props.stayTime}s</div>;
    };
    
    const TimeOnPage = () => {
      const [second, setSecond] = useState(0);
    
      useEffect(() => {
        setTimeout(() => {
          setSecond(second + 1);
        }, 1000);
      }, [second]);
      return (
        <div>
          <Child stayTime={second} />
        </div>
      );
    }
    

    这属于在 TimeOnPage组件内部硬编码,还没有达到封装复用的目标。看看render props怎么做?

    render props

    接上文,在TimeOnPage里定义一个值为函数的prop,想渲染什么组件,在函数里返回即可,函数的参数就是想要共享的state。

    const Child = (props) => {
      return <div>stayTime: {props.stayTime}s</div>;
    };
    
    const TimeOnPage = (props) => {
      const [second, setSecond] = useState(0);
    
      useEffect(() => {
        setTimeout(() => {
          setSecond(second + 1);
        }, 1000);
      }, [second]);
      return <div>{props.render(second)}</div>;
    };
    
    <TimeOnPage render={(stayTime) => <Child stayTime={stayTime} />
    

    其实,render prop 就是一个用于告知组件需要渲染什么内容的函数prop。

    React Router也用到了这项技术。

    <Router>
      <Route path="/home" render={() => <div>Home</div>} />
    </Router>
    

    高阶组件

    高阶组件是一个函数,参数是一个需要被复用的组件A,返回值是一个新的组件N。新组件N是在组件A的基础上做了一些加工,但不会修改组件A本身,只是功能增强。

    假设有一个新闻列表组件长这样:

    const NewList = () => {
      return (
        <div>
          <ul>
            <li>news item</li>
            <li>news item</li>
          </ul>
        </div>
      );
    }
    

    想要在新闻列表加载期间显示loading动画组件 <Loading />,通常会这么做

    const Loading = () => {
      // loading动画
    }
    const NewList = ({ isLoading }) => {
      return isLoading ? (
        <Loading />
      ) : (
        <div>
          <ul>
            <li>news item</li>
            <li>news item</li>
          </ul>
        </div>
      );
    };
    

    假设现在Table组件也要在加载数据期间显示loading动画组件,遵循类似的模式

    const Loading = () => {
      // loading动画
    }
    const DataList = ({ isLoading, ...props }) => {
      return isLoading ? (
        <Loading />
      ) : (
        <Table {...props} />
      );
    };
    

    以上,你会发现DataList和NewList结构极度相似,如果还有第三个、第四个组件要加loading,继续照这个模式重复第三次、第四次吗?这不是最理想的做法,更好的做法是,使用高阶组件把这个模式抽象出来:

    const WithLoading = (WrappedComponent) => {
      return ({isLoading, ...props}) => {
        return isLoading ? <Loading /> : <WrappedComponent {...props} />;
      }
    };
    

    然后就可以在不修改NewList和DataList的情况下分别给他们增加loading

    const NewList = () => {
      return (
        <div>
          <ul>
            <li>news item</li>
            <li>news item</li>
          </ul>
        </div>
      );
    };
    
    const DataList = (props) => {
      return <Table {...props} />
    };
    
    const WithLoading = (WrappedComponent) => {
      return ({isLoading, ...props}) => {
        return isLoading ? <Loading /> : <WrappedComponent {...props} />;
      }
    };
    // 带loading的NewList
    const WithLoadingNewList = WithLoading(<NewList />)
    // 带loading的DataList
    const WithLoadingDataList = WithLoading(<DataList />)
    

    自定义Hook

    React Hook有useState、useEffect等,它们都是函数,自定义Hook也是一个函数,它的名称同样以use开头,函数内部可以调用其它Hook。与React组件不同的是,自定义Hook可以没有返回值。与普通函数不同的是,自定义Hook内部可以调用其它Hook,而普通函数则不行。

    在写业务逻辑过程中,一般会将一些可重用的的方法定义成工具函数,然后就可以到处复用。同样,通过自定义 Hook,可以将组件逻辑提取到可重用的函数中。到底选择自定义Hook还是工具函数,取决于要提取的组件逻辑需不需要用到其他Hook,如果需要,就选择自定义Hook,否则用工具函数即可。

    回到本文第一个 TimeOnPage组件,改成自定义Hook的形式

    const useTimeOnPage = () => {
      const [second, setSecond] = useState(0);
    
      useEffect(() => {
        setTimeout(() => {
          setSecond(second + 1);
        }, 1000);
      }, [second]);
      return second;
    }
    

    使用方法

    const Demo = () => {
      const stayTime = useTimeOnPage();
      return <div>当前页面停留时间:{stayTime}秒</div>
    }
    

    总结

    三种共享组件逻辑的方式有各自的适用场景:

    render props适合共享那些有不同子组件/子元素的父组件,子组件/子元素的“坑位”已经定义好了,只能渲染在指定位置;

    高阶组件适合在不修改原有组件的基础上对组件进行扩展;

    自定义Hook能做的,纯函数基本上也能做,只是有时候用自定义Hook实现会更方便快捷。

    本文链接:Github


    起源地下载网 » React中共享组件逻辑的三种方式

    常见问题FAQ

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

    发表评论

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

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

    联系作者

    请选择支付方式

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