反应:从渲染方法外部更新组件的 props,而不使用状态

React: update component's props from outside of render method without using state

本文关键字:props 状态 组件 方法 更新 外部 反应      更新时间:2023-09-26

这就是我想要实现的目标。我有两个 React 组件ProductProductInfoPanel ,显示在一个ProductList组件中。 Product显示有关产品的选定信息,例如产品名称和价格。单击产品时,更多详细信息将显示在ProductInfoPanel中。所以我需要通过哇哇点击到ProductInfoPanel.

这是我目前将它们连接在一起的方式。每个Product都会得到一个点击处理程序,该处理程序在调用时传回product对象,然后将其传递到ProductInfoPanel的 props 中。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>
    );
  }
}

这是我能想到的最好的,但是使用状态来跟踪点击的产品对我来说仍然听起来是错误的。我的意思是,这不是一个组件的真正状态,是吗?

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

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

你可以使用像 redux 这样的类似 flux 的库,或者像 mobx 这样的替代方案来从组件中删除状态管理,但我个人的感觉是保持它尽可能简单,直到你真的觉得在你的项目中添加另一个抽象层会有显着的好处。

我曾经默认使用 redux 开始项目,但有一次我踢了自己,因为事实证明,引入 redux 实现的额外复杂性对于实际上相当小而简单的项目来说是矫枉过正的。 我不知道是否有一条很难知道什么时候你应该回避使用标准状态并引入另一个库来为你管理它,但我已经了解到,首先以最简单和最简单的方式做这件事可能是最安全的,直到你真正觉得引入另一个依赖项有实际好处。


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

您将函数绑定在属性中,如下所示:

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

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

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

我认为这很好。如果有更多的组件响应SelectedProduct的变化,那么让父组件控制状态的价值将更加明显。在您的情况下,这似乎没有必要,因为只有一个组件更改。

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