浅析为什么用高阶组件代替 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
  }
}

 

posted @ 2018-07-30 15:59  这个男人  阅读(992)  评论(0编辑  收藏  举报