如何在React中使用组合而不是mixins来解决这个问题?

Juh*_*ala 3 reactjs

假设我有一个关于汽车销售的数据集,我正在构建显示图表的组件,这些图表显示了该数据的不同方面.

例如,一个组件可以显示汽车的平均销售价格,而另一个组件可以显示汽缸的数量与去年购买的汽车的里程数之间的关系.

这些组件还可以进一步参数化,以便它们仅显示在给定国家/地区购买的汽车的数据等.似乎使用道具来传递参数和数据似乎是合适的,因此这些组件可以像这样使用:

<MileageByCylinder country="US" year="2014" dataset={data} />
<AvgSellingPricesByYear country="US"  dataset={data} />
Run Code Online (Sandbox Code Playgroud)

到现在为止还挺好.但是,我要说的是,我正在构建两个以不同方式组成这些组件的视图(页面).第一个将静态显示不同国家/地区的销售价格,而另一个将具有用于选择国家/地区的UI以及按柱面显示里程数.两个视图都需要以某种方式获取它们传递给图表组件的数据集.但是如何重新获取获取数据集的逻辑呢?

使用mixins,可以按如下方式完成:

var CarDataMixin = {

  componentDidMount: {
    // fetch car data and
    // call this.setState({carData: fetchedData}),
    // once data has been (asynchronously) fetched
  }

}

var FirstView = React.createClass({

  mixins: [CarDataMixin],

  render: function() {
    return (
        <div>
          <AvgSellingPricesByYear country="US"  dataset={this.state.carData} />
          <AvgSellingPricesByYear country="UK"  dataset={this.state.carData} />
          <AvgSellingPricesByYear country="FI"  dataset={this.state.carData} />
        </div>
      )
   }

})

var SecondView = React.createClass({

  mixins: [CarDataMixin],

  handleNewCountry: function(country) {
    this.state{country: country}
  },

  render: function() {
    return (
        <div>
          <CountryChooser onChange={this.handleNewCountry} />
          <MileageByCylinder country="{country}" year="2014" dataset={this.state.carData} />
          <AvgSellingPricesByYear country="{country}"  dataset={this.state.carData} />
        </div>
      )
   }

})
Run Code Online (Sandbox Code Playgroud)

好的,很好.但是很多人建议不要使用mixins,而应该使用这种成分.使用我提出的构图来解决这个问题的唯一方法如下:

  1. 创建一个将其状态传递给子节点的根组件作为props:

    var CarSalesRoot = React.createClass({
        componentDidMount: {
          // fetch car data and
          // call this.setState({carData: fetchedData}),
          // once data has been (asynchronously) fetched
        }
    
        renderChildren: function () {
            return React.Children.map(this.props.children, function (child) {
              return React.addons.cloneWithProps(child, {
                  carData: this.state.carData
              })
            }.bind(this))
        },
    
    
        render: function() {
          return <div>{this.renderChildren()}</div>
        } 
    });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 在没有mixin的情况下创建视图:

    var FirstView = React.createClass({
        render: function() {
          return (
              <div>
                <AvgSellingPricesByYear country="US"  dataset={this.props.carData} />
                <AvgSellingPricesByYear country="UK"  dataset={this.props.carData} />
                <AvgSellingPricesByYear country="FI"  dataset={this.props.carData} />
              </div>
            )
         }
    });
    
    Run Code Online (Sandbox Code Playgroud)
  3. 创建一个包装根组件和主视图组件的包装器组件:

    var FirstViewMain = React.createClass({
        render: function() {
          return (
              <CarSalesRoot>
                <FirstView />
              </CarSalesRoot>
            )
         }
    });
    
    Run Code Online (Sandbox Code Playgroud)

这感觉有点棘手,并且使数据流不那么明确.我觉得这是非常基本的东西,应该以一种干净的方式解决.我错过了一些明显的东西吗 或者这实际上是一个惯用而干净的解决方案?

小智 6

使用组合的另一个选择是创建一个"高阶组件",它类似于函数式编程中的高阶函数,但实际上它只是一个包装器,它对您呈现的代码略有不同.

  1. 定义高阶组件:

    var bindToCarData = function (Component) {
      return React.createClass({
        componentDidMount: {
          // fetch car data and
          // call this.setState({carData: fetchedData}),
          // once data has been (asynchronously) fetched
        },
    
        render: function() {
          return <Component carData={ this.state.carData } />
        }
      });
    });
    
    Run Code Online (Sandbox Code Playgroud)
  2. 然后在定义组件时包装它.

    var FirstView = bindToCarData(React.createClass({
      render: function() {
        return (
            <div>
              <AvgSellingPricesByYear country="US"  dataset={this.props.carData} />
              <AvgSellingPricesByYear country="UK"  dataset={this.props.carData} />
              <AvgSellingPricesByYear country="FI"  dataset={this.props.carData} />
            </div>
          )
       }
    }));
    
    Run Code Online (Sandbox Code Playgroud)

这使您无需编写额外的组件(问题中的第3个),并且它直接在需要数据的组件中绑定数据流逻辑.

bindToCarData如果需要,可以将其他参数传递给函数.

  • 我还建议将`... this.props`扩展到高阶组件的`component`渲染上,这样分配给包装版本的任何道具仍然会收到它们. (2认同)