浅谈浅谈React中组件逻辑复用的那些事儿中组件逻辑复用的那些事儿
主要介绍了浅谈React中组件逻辑复用的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学
习吧
基本每个开发者都需要考虑逻辑复用的问题,否则你的项目中将充斥着大量的重复代码。那么 React 是怎么复用组件逻辑的呢?本文将一一介绍 React 复用组件逻辑的几种方法,希望你读完之后能够有
所收获。如果你对这些内容已经非常清楚,那么略过本文即可。
我已尽量对文中的代码和内容进行了校验,但是因为自身知识水平限制,难免有错误,欢迎在评论区指正。
1. Mixins
Mixins 事实上是 React.createClass 的产物了。当然,如果你曾经在低版本的 react 中使用过 Mixins,例如 react-timer-mixin, react-addons-pure-render-mixin,那么你可能知道,在 React 的新版本中
我们其实还是可以使用 mixin,虽然 React.createClass 已经被移除了,但是仍然可以使用第三方库 create-react-class,来继续使用 mixin。甚至,ES6 写法的组件,也同样有方式去使用 mixin。当然
啦,这不是本文讨论的重点,就不多做介绍了,如果你维护的老项目在升级的过程中遇到这类问题,可以与我探讨。
新的项目中基本不会出现 Mixins,但是如果你们公司还有一些老项目要维护,其中可能就应用了 Mixins,因此稍微花点时间,了解下 Mixins 的使用方法和原理,还是有必要的。倘若你完全没有这方面的
需求,那么跳过本节亦是可以的。
Mixins 的使用的使用
React 15.3.0 版本中增加了 PureComponent。而在此之前,或者如果你使用的是 React.createClass 的方式创建组件,那么想要同样的功能,就是使用 react-addons-pure-render-mixin,例如:
//下面代码在新版React中可正常运行,因为现在已经无法使用 `React.createClass`,我就不使用 `React.createClass` 来写了。
const createReactClass = require('create-react-class');
const PureRenderMixin = require('react-addons-pure-render-mixin');
const MyDialog = createReactClass({
displayName: 'MyDialog',
mixins: [PureRenderMixin],
//other code
render() {
return (
<div>
{/* other code */}
</div>
)
}
});
首先,需要注意,mixins 的值是一个数组,如果有多个 Mixins,那么只需要依次放在数组中即可,例如: mixins: [PureRenderMixin, TimerMixin]。
Mixins 的原理的原理
Mixins 的原理可以简单理解为将一个 mixin 对象上的方法增加到组件上。类似于 $.extend 方法,不过 React 还进行了一些其它的处理,例如:除了生命周期函数外,不同的 mixins 中是不允许有相同的
属性的,并且也不能和组件中的属性和方法同名,否则会抛出异常。另外即使是生命周期函数,constructor 、render 和 shouldComponentUpdate 也是不允许重复的。
而如 compoentDidMount 的生命周期,会依次调用 Mixins,然后再调用组件中定义的 compoentDidMount。
例如,上面的 PureRenderMixin 提供的对象中,有一个 shouldComponentUpdate 方法,即是将这个方法增加到了 MyDialog 上,此时 MyDialog 中不能再定义 shouldComponentUpdate,否则会抛出异
常。
//react-addons-pure-render-mixin 源码
var shallowEqual = require('fbjs/lib/shallowEqual');
module.exports = {
shouldComponentUpdate: function(nextProps, nextState) {
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState)
);
},
};
Mixins 的缺点的缺点
Mixins 引入了隐式的依赖关系。
例如,每个 mixin 依赖于其他的 mixin,那么修改其中一个就可能破坏另一个。
Mixins 会导致名称冲突
如果两个 mixin 中存在同名方法,就会抛出异常。另外,假设你引入了一个第三方的 mixin,该 mixin 上的方法和你组件的方法名发生冲突,你就不得不对方法进行重命名。
Mixins 会导致越来越复杂
mixin 开始的时候是简单的,但是随着时间的推移,容易变得越来越复杂。例如,一个组件需要一些状态来跟踪鼠标悬停,为了保持逻辑的可重用性,将 handleMouseEnter()、handleMouseLeave() 和
isHovering() 提取到 HoverMixin() 中。
然后其他人可能需要实现一个提示框,他们不想复制 HoverMixin() 的逻辑,于是他们创建了一个使用 HoverMixin 的 TooltipMixin,TooltipMixin 在它的 componentDidUpdate 中读取 HoverMixin() 提供的
isHovering() 来决定显示或隐藏提示框。
几个月之后,有人想将提示框的方向设置为可配置的。为了避免代码重复,他们将 getTooltipOptions() 方法增加到了 TooltipMixin 中。结果过了段时间,你需要再同一个组件中显示多个提示框,提示框
不再是悬停时显示了,或者一些其他的功能,你需要解耦 HoverMixin() 和 TooltipMixin 。另外,如果很多组件使用了某个 mixin,mixin 中新增的功能都会被添加到所有组件中,事实上很多组件完全不需
要这些新功能。
渐渐地,封装的边界被侵蚀了,由于很难更改或移除现有的mixin,它们变得越来越抽象,直到没有人理解它们是如何工作的。
React 官方认为在 React 代码库中,Mixin 是不必要的,也是有问题的。推荐开发者使用高阶组件来进行组件逻辑的复用。
2. HOC
React 官方文档对 HOC 进行了如下的定义:高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React 的组合特性而形成的设计模式。
简而言之,高阶组件就是一个函数,它接受一个组件为参数,返回一个新组件。
高阶组件的定义形如下面这样:
//接受一个组件 WrappedComponent 作为参数,返回一个新组件 Proxy
function withXXX(WrappedComponent) {
return class Proxy extends React.Component {
render() {
return <WrappedComponent {...this.props}>
}
}
}
开发项目时,当你发现不同的组件有相似的逻辑,或者发现自己在写重复代码的时候,这时候就需要考虑组件复用的问题了。
这里我以一个实际开发的例子来说明,近期各大APP都在适配暗黑模式,而暗黑模式下的背景色、字体颜色等等和正常模式肯定是不一样的。那么就需要监听暗黑模式开启关闭事件,每个UI组件都需要
根据当前的模式来设置样式。
每个组件都去监听事件变化来 setState 肯定是不可能的,因为会造成多次渲染。
这里我们需要借助 context API 来做,我以新的 Context API 为例。如果使用老的 context API 实现该功能,需要使用发布订阅模式来做,最后利用 react-native / react-dom 提供的
unstable_batchedUpdates 来统一更新,避免多次渲染的问题(老的 context API 在值发生变化时,如果组件中 shouldComponentUpdate 返回了 false,那么它的子孙组件就不会重新渲染了)。
顺便多说一句,很多新的API出来的时候,不要急着在项目中使用,比如新的 Context API,如果你的 react 版本是 16.3.1, react-dom 版本是16.3.3,你会发现,当你的子组件是函数组件时,即是用
Context.Consumer 的形式时,你是能获取到 context 上的值,而你的组件是个类组件时,你根本拿不到 context 上的值。