React не перезагружает данные компонента при изменении параметров маршрута или изменении запроса

У меня есть "домашний" компонент со ссылками, и при нажатии на ссылку компонент продукта загружается вместе с продуктом. У меня также есть другой компонент, который всегда виден, показывая ссылки на "недавно посещенные продукты".

Эти ссылки не работают, когда на странице продукта. URL обновляется, когда я нажимаю на ссылку, и происходит рендеринг, но компонент продукта не обновляется с новым продуктом.

Посмотрите этот пример: пример Codesandbox

Вот маршруты в index.js:

<BrowserRouter>
  <div>
    <Route
      exact
      path="/"
      render={props => <Home products={this.state.products} />}
    />

    <Route path="/products/:product" render={props => <Product {...props} />} />

    <Route path="/" render={() => <ProductHistory />} />

    <Link to="/">to Home</Link>
  </div>
</BrowserRouter>;

Ссылки в ProductHistory выглядят так:

<Link to={'/products/${product.product_id}'}> {product.name}</Link>

Таким образом, они соответствуют Route path="/products/:product".

Когда я нахожусь на странице продукта и пытаюсь перейти по ссылке ProductHistory, URL обновляется и отображается, но данные компонента не меняются. В примере Codesandbox вы можете раскомментировать предупреждение в функции визуализации компонентов продукта, чтобы увидеть, что оно отображается при переходе по ссылке, но ничего не происходит.

Я не знаю, в чем проблема... Можете ли вы объяснить проблему и найти решение? Это было бы прекрасно!

Ответ 1

Наряду с componentDidMount, вам также нужно реализовать componentWillReceiveProps или использовать getDerivedStateFromProps (начиная с v16.3.0 и далее) на странице Products поскольку тот же компонент re-rendered с обновленными params и not re-mounted при изменении параметров маршрута, это потому, что параметры передаются как подпорки компоненту и при смене реквизита компоненты React перерисовываются и не монтируются.

РЕДАКТИРОВАТЬ: начиная с версии 16.3.0 используйте getDerivedStateFromProps для установки/обновления состояния на основе параметров (не нужно указывать его в двух разных методах жизненного цикла)

static getDerivedStateFromProps(nextProps, prevState) {
   if (nextProps.match.params.product !== prevState.currentProductId){
      const currentProductId = nextProps.match.params.product
      const result = productlist.products.filter(obj => {

        return obj.id === currentProductId;

      })
     return {

        product: result[0],
        currentId: currentProductId,
        result

      }
  }
  return null;
}

До версии 16.3.0 вы должны использовать componentWillReceiveProps

componentWillReceiveProps(nextProps) {
    if (nextProps.match.params.product !== this.props.match.params.product) {
      const currentProductId = nextProps.match.params.product
      const result = productlist.products.filter(obj => {

        return obj.id === currentProductId;

      })
      this.setState({

        product: result[0],
        currentId: currentProductId,
        result

      })
    }
  }

Рабочие коды и коробка

Ответ 2

Поскольку компонент продукта уже загружен, он не перезагружается. Вы должны обрабатывать новый идентификатор продукта в следующем методе компонента

componentWillReceiveProps(nextProps) {
if(nextProps.match.params.name.product == oldProductId){
  return;
}else {
 //fetchnewProduct and set state to reload
}

С последней версией реакции (16.3.0 и далее)

static getDerivedStateFromProps(nextProps, prevState){
   if(nextProps.productID !== prevState.productID){
     return { productID: nextProps.productID};
  } 
  else {
     return null;
  }
}

componentDidUpdate(prevProps, prevState) {
  if(prevProps.productID !== this.state.productID){
     //fetchnewProduct and set state to reload
  }
}

Ответ 3

Если вы не поддерживаете состояние в своем компоненте, вы можете использовать componentDidUpdate без необходимости getDerivedStateFromProps:

  componentDidUpdate(prevProps) {
    const { match: { params: { value } } } = this.props
    if (prevProps.match.params.value !== value){
      doSomething(this.props.match.params.value)
    }
  }

Ответ 4

Хотя все вышеперечисленные способы будут работать, я не вижу смысла использовать getDerivedStateFromProps. Основываясь на документах React, "если вы хотите пересчитать некоторые данные только при смене реквизита, используйте вместо этого помощник по напоминанию".

Здесь вместо этого я бы предложил просто использовать componentDidUpdate вместе с изменением Component на PureComponenet.

Применительно к документам React PureComponenet только при изменении хотя бы одного состояния или значения проп. Изменение определяется путем поверхностного сравнения ключей состояния и реквизита.

  componentDidUpdate = (prevProps) => {
    if(this.props.match.params.id !== prevProps.match.params.id ) {
      // fetch the new product based and set it to the state of the component
   };
  };