React doesn't reload component data on route param change or query change
Solution 1
Along with componentDidMount
, You also need to implement the componentWillReceiveProps
or use getDerivedStateFromProps
(from v16.3.0 onwards) in Products
page since the same component is re-rendered
with updated params
and not re-mounted
when you change the route params, this is because params are passed as props to the component and on props change, React components re-render and not re-mounted.
EDIT: from v16.3.0 use getDerivedStateFromProps
to set/update state based on props(no need to specify it in two different lifecyle methods)
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;
}
Prior v16.3.0, you would use 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
})
}
}
Solution 2
As Product component is already loaded it will not reload. You have to handle new product id in the below method of component
componentWillReceiveProps(nextProps) {
if(nextProps.match.params.name.product == oldProductId){
return;
}else {
//fetchnewProduct and set state to reload
}
With latest version of react(16.3.0 onwards)
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
}
}
Solution 3
Although all the above-mentioned ways will work, I don't see a point to use getDerivedStateFromProps
.
Based on React docs, "if you want to re-compute some data only when a prop changes, use a memoization helper instead".
Here, instead, I would suggest simply using componentDidUpdate
along with changing the Component
to PureComponenet
.
With reference to React docs, PureComponenet
s only rerender if at least one state or prop value changes. Change is determined by doing a shallow comparison of state and prop keys.
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
};
};
Please note that the above only work if you change the Component to PureComponent, and obviously, you need to import it from React.
Solution 4
If you aren't maintaining state in your component, you can use componentDidUpdate
without the need for getDerivedStateFromProps
:
componentDidUpdate(prevProps) {
const { match: { params: { value } } } = this.props
if (prevProps.match.params.value !== value){
doSomething(this.props.match.params.value)
}
}
Galivan
Updated on June 04, 2020Comments
-
Galivan almost 4 years
I have a "home" component with links, and when you click a link the product component is loaded with the product. I also have another component which is always visible, showing links to the "recently visited products".
These links don't work when on a product page. The url updates when I click the link, and a render occurs, but the product component doesn't update with the new product.
See this example: Codesandbox example
Here are the routes in 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>;
The links in ProductHistory look like this:
<Link to={`/products/${product.product_id}`}> {product.name}</Link>
So they match the
Route path="/products/:product"
.When I am on a product page and try to follow a ProductHistory link, the URL updates and a render occurs, but the component data doesn't change. In the Codesandbox example you can uncomment the alert in Product components render function to see that it renders when you follow the link, but nothing happens.
I don't know what the problem is...Can you explain the problem and find a solution? That would be great!
-
Bastian Voigt over 5 yearscomponentWillReceiveProps is now deprecated. But how else can you do it?
-
Bastian Voigt over 5 yearsComponentWillReceiveProps is also deprecated and considered unsafe now... is there any other way to do it?
-
Prakash S over 5 years@BastianVoigt it should be prevProps as componentDidUpdate will be called after props has changed which will be same nextProps which we had set in getDerivedStateFromProps.
-
Bastian Voigt over 5 yearsRight, but I would want to reload when the current productID (this.props.productID) is different from the one in my state, don't you think?
-
Prakash S over 5 years@BastianVoigt. Right that is what above condition does. The current productID(this.props.producID) will be having new porductID which we have had said in state variable. If we compare that then it will be always equal
-
Arash Khajelou over 5 years@BastianVoigt : You can use integration of new lifecycle methods in react +16.3
getDerivedStateFromProps
,componentDidUpdate
as described in this answer. -
developarvin over 5 yearsI tried the
getDerivedStateFromProps
solution. But it didn't work. It seems that thegetDerivedStateFromProps
only fires once on mount. -
Shubham Khatri over 5 years@arvinsim, Not true, getDerivedStateFromPRops is fired on initial mount as well as on every other update. Also there was a change in this API in 16.4. I will update my answer to incorporate that change
-
Moiz about 5 yearsThis pointed me in the right direction, thanks for the great answer.
-
Siddharth Gharge over 4 yearsIn my case it's working for regular components too.
-
Alexis Lessard over 3 yearsBut how can we make this work with functionnal components, now?