这三者是目前 react 解决代码复用问题的主要方式:
一. HOC 高阶组件
HOC自身不是React API,它是一种基于React 的组合特性而形成的设计模式。具体而言,高阶组件是参数为组件,返回值为新组件的函数。
栗如:现在有一个 CommentList 组件,它订阅外部资源,渲染评论列表!!
class CommentList extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
// 假设 "DataSource" 是个全局范围内的数据源变量
comments: DataSource.getComments()
};
}
componentDidMount() {
// 订阅更改
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
// 清除订阅
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
// 当数据源更新时,更新组件状态
this.setState({
comments: DataSource.getComments()
});
}
render() {
return (
<div>
{this.state.comments.map((comment) => (
<Comment comment={comment} key={comment.id} />
))}
</div>
);
}
}
稍后,编写了一个用于订阅单个博客帖子的组件,该帖子遵循类似的模式:
class BlogPost extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
blogPost: DataSource.getBlogPost(props.id)
};
}
componentDidMount() {
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
blogPost: DataSource.getBlogPost(this.props.id)
});
}
render() {
return <TextBlock text={this.state.blogPost} />;
}
}
会发现,这两个组件有异曲同工之妙,那我们就要思考,是不是可以将它们的共同逻辑抽象出来,然后实现代码复用!!!【此时就可以使用高阶组件,定义一个高阶组件函数,去分别包装它们两个,在高阶组件中实现资源的订阅,并直接将数据返回给它们!!!!】
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
优点:
- 实现逻辑复用、不影响被包裹组件的内部逻辑。
缺点:
- hoc传递给被包裹组件的props容易和被包裹后的组件原有的props重名,进而被覆盖
二. Render Props
Render Props原理为:给组件传一个 props 属性 ---> render 【也可以是任意其他名】,该属性为一个函数,在里面定义一些组件需要独特渲染的内容。然后在组件定义的内部,去this.props.render()调用,从而进行渲染。
栗子:
如上,为 antd 组件库,使用该方法进行封装的 < Table > 组件 ,将其作为属性传给组件,并在组件内部调用!!!
优点:
- 数据共享、代码复用,将组件内的state作为props传递给调用者,将渲染逻辑交给调用者。
缺点:
- 嵌套写法不够优雅
三. hooks
hooks是React 16.8的新增特性。通过将可复用的代码抽象出来定义成一个hook函数,从而复用代码逻辑。
如上,将订阅外部资源的代码抽离出来,定义一个函数,然后在需要使用的组件内部直接调用!!!
优点:
- 使用直观
- 不会出现嵌套地域问题
缺点:
- hook 只能在组件顶层使用,不能在分支语句中使用