浅析为什么用高阶组件代替 Mixins
转载来源 感谢分享
Mixins 引入了无形的依赖
- 应尽量构建无状态组件,Mixin 则反其道而行之
- Mixin 可能会相互依赖,相互耦合,不利于代码维护
- 不同的 Mixin 中的方法可能会相互冲突
举个例子
Mixins 有个主要用途是组件从其它数据源订阅数据:
class CommentList extends React.Component { 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> ) } }
当多个组件共用此逻辑时,使用 高阶组件 来优化:
const CommentListWithSubscription = withSubscription( CommentList, (DataSource) => DataSource.getComments() ) const BlogPostWithSubscription = withSubscription( BlogPost, (DataSource, props) => DataSource.getBlogPost(props.id) ) // 函数接受一个组件参数…… function withSubscription(WrappedComponent, selectData) { // ……返回另一个新组件…… return class extends React.Component { 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() { // ……使用最新的数据渲染组件 // 注意此处将已有的props属性传递给原组件 return <WrappedComponent data={this.state.data} {...this.props} /> } } }
高阶组件既不会修改 input 原组件,也不会使用继承复制 input 原组件的行为。相反,高阶组件是通过将原组件包裹(wrapping)在容器组件(container component)里面的方式来组合(composes)使用原组件。高阶组件就是一个没有副作用的纯函数。
抽取状态
说白了就是在无状态组件和其容器组件的关系中,通常让无状态组件不管理自己的状态,状态的管理交给外面的容器组件。
react-redux 的 connect 函数就用来抽取状态,它返回一个高阶组件。
假定你了解 Redux,那么可以看下 connect 的简易实现:
const doNothing = () => ({}) function connect(mapStateToProps=doNothing, mapDispatchToProps=doNothing) { return function(WarppedComponent) { class HOC extends React.Component { this.state = {} componentDidMount() { this.context.store.subscribe(this.onChange) } componentWillUmount() { this.context.store.unsubscribe(this.onChange) } // HOC 保证 Redux 上 Store 状态变化的时候驱动这个组件的更新 onChange = () => this.setState({}) render() { const store = this.centext.store // 执行 getState 得到 Redux Store 状态 // 通过 dispatch 得到传递给 mapDispatchToProps 的 dispatch 方法 const newProps = { ...this.props, ...mapStateToProps(store.getState()), ...mapDispatchToProps(store.dispatch) } return <WarppedComponent {...newProps} /> } } // 创建新组件需要利用 Context 功能,所以定义了 types 属性, // 从 Context 中获取名为 store 的值 HOC.contextTypes = { store.React.PropTypes.object } return HOC } }