反应容器可重用性(使用Redux,Immutable和Reselect)

时间:2018-01-24 12:17:04

标签: reactjs redux react-redux reactive-programming code-reuse

我有一个用例,我有一个容器,比如说BarChartContainer。此容器将URL作为prop,并且在此URL的基础上,我的saga获取所需的数据,并将其发送到BarChartComponent(PureComponent),该BarChartComponent从此BarChartContainer调用以呈现条形图。 现在,我希望这是动态的,如果我想在3个地方的容器中使用它,我只需要传递所需URL的支柱,并且容器的每个实例都应该根据URL呈现数据。我面临的问题是,尽管每次调用此容器都有不同的道具,但容器只有一个商店。所以最终,只有1个URL存储在商店中(即来自BarChartContainer第三个实例的道具的URL。我应该如何配置商店,以便它维护树,以保持实例的状态。

这是我的容器的代码

export class BarChartContainer extends React.PureComponent { 
  static propTypes = {
    initializeBarChart: PropTypes.func,
    data: PropTypes.object.isRequired,
    dataUrl: PropTypes.string,
    addUrlToStore: PropTypes.func,
  };
  /**
   *Defines the default values of certain props
   *
   * @static
   * @memberof BarChartContainer
   */
  static defaultProps = {
    data: {},
    dataUrl: '',
    };
  /**
   *This function is called on the initial load of BarChartContainer
   *
   * @memberof BarChartContainer
   */
  componentWillMount() {
    this.props.addUrlToStore(this.props.dataUrl);
    this.props.initializeBarChart();
  }
  render() {
    return (
      <div className="container-barchartcontainer">
        <BarChart data={this.props.data.dataPoints} />
      </div>
    );
  }
}

const mapStateToProps = createStructuredSelector({
  data: selectData(),
});

function mapDispatchToProps(dispatch) {
  return {
    initializeBarChart: () => dispatch(defaultAction()),
    addUrlToStore: (url) => dispatch(addUrlToStoreAction(url)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'barChartContainer', reducer });
const withSaga = injectSaga({ key: 'barChartContainer`, saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(BarChartContainer);

这里是三个容器的持有者

    export default class Holder extends React.PureComponent {
      render() {
        return (
          <div className="container-holder">
            <BarChartContainer dataUrl='url1' />

            <BarChartContainer dataUrl='url2' />

            <BarChartContainer dataUrl='url3' />
          </div>

        );

  }
}

1 个答案:

答案 0 :(得分:0)

为了达到你想要的效果,(IMO)你必须重新思考模型/过程。

与商店绑定的容器不可重复使用,如果它们代表商店的不同部分,它们不应该是。在这种情况下,因为数据是不同的声音,因为它们实际上代表了商店的不同部分。所以可重用的部分只是渲染部分,但没有别的。

如果是这种情况,那么(IMO)我会为我需要表示的商店的每个部分创建一个容器,我也会创建用于呈现数据的组件。

由于每个图形代表语义上不同的东西,因此在简单性和可伸缩性方面分别获得它们。

另一点是,由于数据来自不同的来源,每个容器可能需要不同的方式来处理和管理与其各自来源的通信(例外,身份验证,授权等)。

选项1:

在你的情况下,我会创建三个主要容器和一个BarChartContainer:

// If this URL is likely going to be hardcoded, then It does not make any sense to be passed as a prop.
// If this URL comes from a dynamic source, then it should have already been injected to the store when the URL was got
const dataUrl = 'http://url.com'; 

class ParticularBussinessBarChart extends React.Component { 
  componentWillMount() {
    this.props.addUrlToStore(dataUrl); //This could not make sense
    this.props.initializeParticularBussinessBarChart(); // Why not injecting the url here directly?
  }

  render() {
    return (
        <BarChartContainer dataPoints={this.props.data.dataPoints} />
    );
  }
}

const mapStateToProps = createStructuredSelector({
  data: selectData(),
});

function mapDispatchToProps(dispatch) {
  return {
    initializeParticularBussinessBarChart: () => dispatch(defaultAction()),
    addUrlToStore: (url) => dispatch(addUrlToStoreAction(url)),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'particularBussinessBarChart', reducer });
const withSaga = injectSaga({ key: 'particularBussinessBarChart`, saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(ParticularBussinessBarChart);

然后我会有一个可重用的组件来负责渲染图形,但没有别的。

//This class might become useless with this workaround
export class BarChartContainer extends React.PureComponent { 
  render() {
    return (
      <div className="container-barchartcontainer">
        <BarChart data={this.props.dataPoints} />
      </div>
    );
  }
}

用法是:

export default class Holder extends React.Component {
  componentWillMount() {
    this.props.addUrlToStore(dataUrl); //This could not make sense
    this.props.initializeParticularBussinessBarChart(); // Why not injecting the url here directly?
  }

  render() {
    return (
      <div className="container-holder">
        <ParticularBussinessBarChart/>

        <OtherParticularBussinessBarChart />

        <AnotherParticularBussinessBarChart />
      </div>
    );
  }
}

选项2:

另一个选择是让一个Holder负责获取并分别将相应的数据传递给每个图表。 这个选项在开始时会更简单,但如果添加更多图表,可能会变得更加复杂。

export class BarChartContainer extends React.PureComponent { 
  render() {
    return (
      <div className="container-barchartcontainer">
        <BarChart data={this.props.dataPoints} />
      </div>
    );
  }
}

用法是:

class Holder extends React.Component {
  componentWillMount() {
    this.props.initializeBarChart1('url1');
    this.props.initializeBarChart2('url2');
    this.props.initializeBarChart3('url3');
  }

  render() {
    return (
      <div className="container-holder">
        <BarChartContainer dataPoints={this.props.dataPointsChart1} />

        <BarChartContainer dataPoints={this.props.dataPointsChart2} />

        <BarChartContainer dataPoints={this.props.dataPointsChart3} />
      </div>
    );
  }
}

const mapStateToProps = createStructuredSelector({
  dataPointsChart1: selectDataForChar1(),
  dataPointsChart2: selectDataForChar2(),
  dataPointsChart3: selectDataForChar3(),
});

function mapDispatchToProps(dispatch) {
  return {
    initializeBarChart1: () => dispatch(correspondingAction()),
    initializeBarChart2: () => dispatch(correspondingAction()),
    initializeBarChart3: () => dispatch(correspondingAction()),
  };
}

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'holder', reducer });
const withSaga = injectSaga({ key: 'holder`, saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(ParticularBussinessBarChart);

我更愿意选择第一个选项,因为它似乎对我来说更具可扩展性和简单性。但这实际上取决于你提供的图表的业务和性质。