高阶组件(HOC)是 React 中用于复用组件逻辑的一种高级技巧。HOC 自身不是 React API 的一部分,它是一种基于 React
的组合特性而形成的设计模式。
高阶组件本质上就是一个函数(HOC 是纯函数,没有副作用),接收一个组件后返回另外一个新的组件,这么做的意图就是为了增强(enhance),比如装饰、增添行为、增添逻辑等,都可以放在高阶组件中,高阶组件是一种可以增强组件的能力。
具体而言,高阶组件是参数为组件,返回值为新组件的函数。
const EnhancedComponent = higherOrderComponent(WrappedComponent);
HOC 在 React 的第三方库中很常见,例如 Redux 的 connect 和 Relay 的 createFragmentContainer。
conect:连接React组件与 Redux store。
import { connect } from 'react-redux';
import { testCreators } from 'redux';
...
const mapStateToProps = state => {
return {
testDate:state.appStore.CommonStore.testDate
};
}
const mapDispatchToProps = dispatch =>{
return {
actions: bindActionCreators(Object.assign({}, testCreators), dispatch)
}
}
testComponent.contextTypes = {
router: PropTypes.shape({
history: PropTypes.object.isRequired,
})
};
export default connect(mapStateToProps,mapDispatchToProps)(testComponent);
当你的组件之间出现重复的模式 / 逻辑的时候。
如
我们之前建议使用 mixins 用于解决横切关注点相关的问题。但我们已经意识到 mixins 会产生更多麻烦。
组件是 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 (
{this.state.comments.map((comment) => (
))}
);
}
}
稍后,编写了一个用于订阅单个博客帖子的组件,该帖子遵循类似的模式:
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 ;
}
}
CommentList 和 BlogPost 不同 - 它们在 DataSource 上调用不同的方法,且渲染不同的结果。
但它们的大部分实现都是一样的:
你可以想象,在一个大型应用程序中,这种订阅 DataSource 和调用 setState 的模式将一次又一次地发生。我们需要一个抽象,允许我们在一个地方定义这个逻辑,并在许多组件之间共享它。这正是高阶组件擅长的地方。
对于订阅了 DataSource 的组件,比如 CommentList 和 BlogPost,我们可以编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数,该子组件将订阅数据作为 prop。让我们调用函数 withSubscription:
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
const BlogPostWithSubscription = withSubscription(
BlogPost,
(DataSource, props) => DataSource.getBlogPost(props.id)
);
第一个参数是被包装组件。第二个参数通过 DataSource 和当前的 props 返回我们需要的数据。
当渲染 CommentListWithSubscription 和 BlogPostWithSubscription 时, CommentList 和 BlogPost 将传递一个 data prop,其中包含从 DataSource 检索到的最新数据:
// 此函数接收一个组件...
function withSubscription(WrappedComponent, selectData) {
// ...并返回另一个组件...
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
componentDidMount() {
// ...负责订阅相关的操作...
DataSource.addChangeListener(this.handleChange);
}
componentWillUnmount() {
DataSource.removeChangeListener(this.handleChange);
}
handleChange() {
this.setState({
data: selectData(DataSource, this.props)
});
}
render() {
// ... 并使用新数据渲染被包装的组件!
// 请注意,我们可能还会传递其他属性
return ;
}
};
}
再来个例子
高阶组件:
//定义高阶组件 //放在common公用方法里面
import React, { Component} from 'react';
export default (WrappedComponent) => {
return class extends Component {
constructor(props) {
super(props)
this.state = { //定义可复用的状态
count:1
}
this.getCode = this.getCode.bind(this)
}
componentDidMount() {
alert('111')
}
//定义可复用的方法
getCode(mobile) {
// ...
this.setState({count : mobile})
console.log(mobile)
}
postVcode(mobile) {
// ...
}
render() {
return (
)
}
}
}
// 高阶组件就是把可复用的逻辑放在定义的高阶组件中,然后把需要调用高阶组件的组件作为参数传给高阶组件,然后在高阶组件中
// 实例化该组件,再把需要复用的方法和state传给次组件,然后在此组件中的props中就可以拿到高阶组件里定义的逻辑方法和state了
传入的组件:
import React, { Component} from 'react';
import HOC from './index'
//高阶组件的使用
class Register extends Component{
render() {
return (
{this.props.state.count}
)
}
}
export default HOC(Register)
//高阶组件其实就是将复用的方法传给组件,使代码简洁、代码量更少
比较好的做法:
糟糕的做法:
注意:
永远不要在 React render() 方法中定义 React 组件(甚至是无状态组件)。React 在每次更新状态的时候,都会废弃旧的 html DOM 元素并将其替换为全新的元素。比如在 render() 函数中定义一个输入组件,元素被替换之后就会失去焦点,每次只能输入一个字符。
本文链接https://blog.csdn.net/qq_39903567/article/details/115382231