React:在不使用状态

时间:2016-07-10 10:59:01

标签: javascript reactjs

这是我想要实现的目标。我有两个React组件ProductProductInfoPanel,显示在ProductList组件中。 Product显示有关产品的所选信息,例如产品名称和价格。点击产品后,ProductInfoPanel会显示更多详细信息。所以我需要点击wah twas点击ProductInfoPanel

以下是我目前如何将它们连接在一起。每个Product都会获得一个传入的点击处理程序,它会在调用时传回product个对象,然后传递给ProductInfoPanel的道具。 ProductList使用状态来跟踪点击的内容,因此当它发生变化时,会触发重新呈现信息面板。

class ProductList extends React.Component {
  render() {
    return (
      <div>

        <div className='content'>

          <ul>
            { this.props.products.map((product, index) => {
              return (
                <li key={index}>
                  <Product product={product}
                    clickHandler={this.onProductClicked.bind(this)}/>
                </li>
              );
            })}
          </ul>

        </div>

        <div className='side-panel'>
          <ProductInfoPanel product={this.state.selectedProduct} />
        </div>

      </div>
    );
  }

  onProductClicked(clickedProduct) {
      // Use the product object that was clicked, and updates the state.
      // This updates the info panel content.
      this.setState({ selectedProduct: clickedProduct });
  }

}

这里大致是如何构建这两个组件。

class Product extends React.Component {
  render() {
   // Even though it needs only name and price, it gets the whole product
   // object passed in so that it can pass it to the info panel in the
   // click handler.
    return (
      <div onClick={this.onClicked.bind(this)}>
        <span>{this.props.product.name}</span>
        <span>{this.props.product.price}</span>
      </div>
    );
  }

  onClicked(e) {
    this.props.clickHandler(this.props.product);
  }
}

class ProductInfoPanel extends React.Component {
  render() {
    // Info panel displays more information about a product.
    return (
      <ul>
        <li>{this.props.product.name}</li>
        <li>{this.props.product.price}</li>
        <li>{this.props.product.description}</li>
        <li>{this.props.product.rating}</li>
        <li>{this.props.product.review}</li>
      </ul>
    );
  }
}

这是我能想到的最好的,但使用状态来记录所点击的产品对我来说仍然是错误的。我的意思是,它不是一个组件的状态,是吗?

如果我可以从props方法之外更新引用的React组件的render,那么我尝试将ProductInfoPanel的引用传递给每个Product 1}},所以他们可以在他们的点击处理程序中更新它。

有没有办法实现我想要的东西,并避免使用状态来跟踪被点击的内容?

2 个答案:

答案 0 :(得分:3)

您可以使用像redux之类的类似通量的库,或像mobx这样的替代方法来移除组件中的状态管理,但我个人的感觉是尽可能保持简单,直到您真正感觉在项目中添加另一层抽象会有很大的好处。

我曾经默认使用redux开始项目但是有一次我踢了自己,因为事实证明引入redux实现的额外复杂性对于实际上相当小而简单的项目来说是过度杀伤。我不知道是否有一条强硬路线要知道你什么时候应该回避使用标准状态并引入另一个库来为你管理它,但我已经知道,最简单,最简单的方法可能是最安全的。你真的觉得带来另一种依赖会带来实际好处。

关于您当前代码的一些建议......

您正在如下所示的属性中绑定您的函数:

<Product product={product} clickHandler={this.onProductClicked.bind(this)}/>

当你调用函数bind时,它实际上会返回一个新的函数实例,因此React的调和程序会将它视为一个新的道具进入你的组件,因此总是会重新渲染子组件树。需要注意的事情。作为替代方法,您可以在构造函数中进行早期绑定,如下所示:

class ProductList extends React.Component {
  constructor(props) {
    super(props);
    this.onProductClicked = this.onProductClicked.bind(this);
  }
  render() {
    ...
        <li key={index}>
          <Product product={product}
            clickHandler={this.onProductClicked}/>
        </li>
    ...
  }
}

此外,如果您提供index作为上述唯一key道具,则应考虑使用product模型中的唯一标识符(如果可用)。这样,如果您在列表中添加或删除项目,React将获得更多信息,以了解它是否应该重新呈现所有Product组件实例。

例如:

render() {
  ...
    { 
      this.props.products.map((product) => 
        <li key={product.id}>
          <Product product={product}
            clickHandler={this.onProductClicked}/>
        </li>
      )
    }

  ...
}

在此处阅读有关这些概念的更多信息: https://facebook.github.io/react/docs/advanced-performance.html

答案 1 :(得分:1)

我认为没关系。如果有更多组件响应SelectedProduct中的更改,那么让父组件控制状态的值会更明显。在您的情况下,似乎没有必要,因为只有一个组件发生变化。

但是,如果您的Product也通过突出显示SelectedProduct并且某个RecentlyViewedProducts列表以某种方式回复SelectedProduct来回复,那么很明显SelectedProduct不是ProductInfoPanel的状态,而是它是观察者的应用程序的更高级别部分的状态。