Reputation: 43
I have the following components structure:
Main Route:
<div>
<Header />
<Switch>
<Route exact path="/" render={() => <Home />} />
<Route path="/category" render={() => <Categories />} />
<Route path="/products" render={() => <Products />} />
</Switch>
</div>
The Route in Products:
const productsData = [...];
return (
<div>
<div>
<ProductList data={productsData} />
</div>
<Switch>
<Route path={`${match.url}/:productId`} render={()=>
<ProductDetail data={productsData} />} />
<Route exact path={match.url} render={()=> (
<div style={{ textAlign: "center" }}>Select a product.</div>
)} />
</Switch>
</div>
);
And the route in ProductDetail:
return (
<>
<div>
<h3> {product.name} </h3>
Links:
<div>
<NavLink activeClassName="active" to={`${match.url}/detail/${ "100"}`}>
Click me
</NavLink>
</div>
</div>
<div>
<Route path={`${match.url}/detail/:code`} render={()=>
<ProductDetailMore />} />
</div>
</> );
When I click on 'Click me' the value 100 is correctly displayed in my ProductDetailMore component but all the components are re-rendered (Products, ProductList, ProductDetail, ProductDetailMore); so, my questions is, how can I prevent a re-rendering in the parent components [Products, ProductDetail]?
And especially, I would like to avoid a re-render in ProductList, the one that is not in a Route?
Upvotes: 4
Views: 4583
Reputation: 539
You cannot really generally avoid rerenders, as react already decides which components need a rerender. However, this only means you can't avoid rerendering the parent components. With the react dev tools you can analyze why they rerender (it's an option in the profiler) and possibly find unnecessary causes for rerenders.
But these are the good news:
What you can easily do is preventing sub-components to rerender. For Example "ProductList". One Way would be the React.memo HOC used directly in the export of the component or in the "Categroies" component (at a static location, not in the render function):
const ProductListMemo = React.memo(ProductList)
Then, you're using "ProductListMemo" in the render function. This way, when the component is rendered, React checks beforehand if any props changed. If not, the component is not rerendered. Your code is somehow incomplete though. You define
const productsData = [...]
If this is in the render function, a new array will always be created and even if the contents are the same, React.memo will see a new array and rerender the component. You have to move the array outside of the render function or you have to wrap it in a useMemo (if you're not using class components):
const productsData = useMemo(() => [...], []);
This "useMemo" hook can also be used to avoid rerenders of components, you could use
{useMemo(() => (<div>
<ProductList data={productsData} />
</div>), [productsData])}
This way, every rerender react checks if "productsData" changed, and only then rerenders the components.
So the important thing to know is that if a parent component rerenders because of a state update for example, it will rerender every child component. And these will also rerender every of their child components. With a React.memo or a useMemo however, you can help react to decide to use a previously rendered component instead.
Upvotes: 2