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
};
};