详解React中共享组件逻辑的三种方式


Posted in Javascript onFebruary 02, 2021

废话少说,这三种方式分别是: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

“render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术

接上文,在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>

高阶组件

高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。

高阶组件是一个函数,参数是一个需要被复用的组件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

Hook 是 React 16.8 的新增特性。它可以让你在不编写 class 的情况下使用 state 以及其他的 React 特性。

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中共享组件逻辑的三种方式的文章就介绍到这了,更多相关React 共享组件逻辑内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
Firefox outerHTML实现代码
Jun 04 Javascript
Node.js 制作实时多人游戏框架
Jan 08 Javascript
微信JSSDK上传图片
Aug 23 Javascript
JS常见问题之为什么点击弹出的i总是最后一个
Jan 05 Javascript
Javascript中浏览器窗口的基本操作总结
Aug 18 Javascript
浅析jsopn跨域请求原理及cors(跨域资源共享)的完美解决方法
Feb 06 Javascript
javascript history对象详解
Feb 09 Javascript
ES6中class类用法实例浅析
Apr 06 Javascript
快速掌握jquery分页插件jqPaginator的使用方法
Aug 09 jQuery
利用d3.js力导布局绘制资源拓扑图实例教程
Jan 08 Javascript
常见的浏览器存储方式(cookie、localStorage、sessionStorage)
May 07 Javascript
vue 中使用 watch 出现了如下的报错的原因分析
May 21 Javascript
详解微信小程序轨迹回放实现及遇到的坑
Feb 02 #Javascript
学习 Vue.js 遇到的那些坑
Feb 02 #Vue.js
Vue常用API、高级API的相关总结
Feb 02 #Vue.js
JavaScript实现点击自制菜单效果
Feb 02 #Javascript
Vue项目打包部署到apache服务器的方法步骤
Feb 01 #Vue.js
如何在vue中使用video.js播放m3u8格式的视频
Feb 01 #Vue.js
Vue 实现可视化拖拽页面编辑器
Feb 01 #Vue.js
You might like
php数组函数序列之array_push() 数组尾部添加一个或多个元素(入栈),返回新长度。
2011/11/07 PHP
PHP中的函数-- foreach()的用法详解
2013/06/24 PHP
PHP后台微信支付和支付宝支付开发
2017/04/28 PHP
php解决安全问题的方法实例
2019/09/19 PHP
laravel框架模型、视图与控制器简单操作示例
2019/10/10 PHP
jQuery EasyUI API 中文文档 - ValidateBox验证框
2011/10/06 Javascript
简单的JavaScript互斥锁分享
2014/02/02 Javascript
jQuery实现鼠标单击网页文字后在文本框显示的方法
2015/05/06 Javascript
JavaScript数据库TaffyDB用法实例分析
2015/07/27 Javascript
jQuery Uploadify 上传插件出现Http Error 302 错误的解决办法
2015/12/12 Javascript
学习Bootstrap滚动监听 附调用方法
2016/07/02 Javascript
javaScript给元素添加多个class的简单实现
2016/07/20 Javascript
高效Web开发的10个jQuery代码片段
2016/07/22 Javascript
利用vue写todolist单页应用
2016/12/15 Javascript
获取IE浏览器Cookie信息的方法
2017/01/23 Javascript
js模仿微信朋友圈计算时间显示几天/几小时/几分钟/几秒之前
2017/04/27 Javascript
Bootstrap + AngularJS 实现简单的数据过滤字符查找功能
2017/07/27 Javascript
jQuery实现仿京东防抖动菜单效果示例
2018/07/06 jQuery
js+for循环实现字符串自动转义的代码(把后面的字符替换前面的字符)
2020/12/24 Javascript
Python中的异常处理简明介绍
2015/04/13 Python
Python矩阵常见运算操作实例总结
2017/09/29 Python
11个Python3字典内置方法大全与示例汇总
2019/05/13 Python
python+opencv实现摄像头调用的方法
2019/06/22 Python
python 缺失值处理的方法(Imputation)
2019/07/02 Python
python NumPy ndarray二维数组 按照行列求平均实例
2019/11/26 Python
pytorch 常用线性函数详解
2020/01/15 Python
解决TensorFlow GPU版出现OOM错误的问题
2020/02/03 Python
OpenCV4.1.0+VS2017环境配置的方法步骤
2020/07/09 Python
Python绘制组合图的示例
2020/09/18 Python
python-图片流传输的思路及示例(url转换二维码)
2020/12/21 Python
美国网上鞋子零售商:Dr. Scholl’s Shoes
2017/11/17 全球购物
计算机售后服务承诺书
2014/05/30 职场文书
与美同行演讲稿
2014/09/13 职场文书
检讨书模板大全
2015/05/07 职场文书
保护地球的宣传语
2015/07/13 职场文书
python process模块的使用简介
2021/05/14 Python