3

If I have a root store component and within it I embed a product component which renders products using the routes /product/:id, is it normal behaviour that the root, /, renders every time I change product?

import React, {Component} from "react";
import ReactDOM from "react-dom";

import { BrowserRouter, Route, Link } from "react-router-dom";


const Products = [
  { "id" : "First", info : "Great product"},
  { "id" : "Second", info : "Another Great product"},
  { "id" : "Third", info : "Some other product"},
  { "id" : "Fourth", info : "Worst product"},
]

class ProductDetail extends Component {
  render(){
    console.log("rendering ProductDetail");
    const {match} = this.props;
    const product = Products.find(({id}) => id === match.params.productId);
    return <div>
      <h3>{product.id}</h3>
      <span>{product.info}</span>
    </div>
  }
}

class Product extends Component {
  render(){
  console.log("rendering Product");
    const {match} = this.props;
    return <div>
      <h2>This shows the products</h2>
      <ul>
        {Products.map(p=><li><Link to={`${match.url}/${p.id}`}>{p.id}</Link></li>)}
      </ul>
      <Route path={`${match.path}/:productId`} component={ProductDetail}/>
    </div>
  }
}

class Store extends Component {
   render(){
  console.log("rendering Store");
     const {match} = this.props;
     return <div>
       <h1>This is the Store</h1>
        <Link to={`${match.url}product`}>See products</Link>
       <Route path={`${match.path}product`} component={Product}/>
     </div>
   }
}

function App() {
  console.log("rendering App");
  return (
    <div className="App">
      <BrowserRouter>
        <Route path="/" component={Store}/>
      </BrowserRouter>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

With this sample code, the root store will re-render every time when changing from any /product/* to another /product/*.

Is there a recommended way to prevent the root from re-rendering if only the children are changing?

I'm using react-router v5. You can test the code here

Victor.dMdB
  • 999
  • 2
  • 11
  • 29
  • The question has been asked here "https://stackoverflow.com/questions/48314909/react-router-nested-routes-and-parent-re-render?rq=1" but the answers don't work with the code above. – Victor.dMdB Jun 19 '19 at 10:51
  • Did you ever find a solution to this problem? – Elliot B. Nov 03 '22 at 05:42

2 Answers2

0
<BrowserRouter>
    <Route exact path="/" component={Store}/>
  </BrowserRouter>


 // I think its solve your issue add 'exact'
Tushar
  • 306
  • 5
  • 14
  • 2
    this will mean all nested routes won't be able to render. You can test it out here https://codesandbox.io/embed/blissful-morse-c7r6f – Victor.dMdB Jun 19 '19 at 11:49
0

You could override shouldComponentUpdate method in Store (optional) and Product components. Return false if you consider the specific component should not be updated.

class Product extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Make your own comparisons (returning false will do the trick for your case)
    return false;
  }
...

class Store extends Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Make your own comparisons (returning false will do the trick for your case)
    return false;
  }
...

I'm not sure why, but ProductDetail component will be updated even though Product.shouldComponentUpdate returns false. I guess it has to do with the fact that ProductDetail is part of a Route component...

For Hooks, you could use React.memo like this:

const Store = React.memo((props) => {
   ...
}, (prevProps, nextProps) => {
  // Comment below is from React documentation.
  /*
   return true if passing nextProps to render would return
   the same result as passing prevProps to render,
   otherwise return false
  */
  return true;
});
Daniel Muñoz
  • 1,374
  • 12
  • 18