Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

URL change without re rendering in React router

I am working on an application where I need to change the url of the page without causing a re-render.

I am making a call to fetch all the pages inside componentDidMount like this

componentDidMount() {    
    axios.get('/pages')
      .then(response => {
        this.setState({
          pages: response.data,
          slugs: response.data.pages.map(item => Slug(item.title))
        })
      })

Once I have all the pages then I want to show the individual pages. For eg. in the url it has to be like /page/1/slug-for-page-1 and /page/2/slug-for-page-2 based on a state variable activePageNumber and the active page content will be displayed. Also the corresponding slug should be there on the url.

I tried adding a callback after setState like below, but it didn't work.

() => this.props.history.push({
          pathName: '/page',
          state: {activePageNumber : activePageNumber},
        })

I thought of adding a child component to manage the activePageNumber state, but I am not sure if its the best way to do it and whether it'll prevent the re-render.

I can implement it such that each page make a separate request to the backend, but I wish to call it only once, and then update the url based on the active page number.

I am not sure how to implement this in react.

I have the following versions

"react-router-dom": "^5.0.0"

"react": "^16.8.6"

like image 589
Shahrukh Mohammad Avatar asked May 09 '19 06:05

Shahrukh Mohammad


1 Answers

If you define your /page route like this:

<Route
  path="/page/:number"
  component={({ match }) => (
    <Pages activePageNumber={match.params.number} />
  )}
/>

The Pages component method componentDidMount will be called when you switch from, for example, /home to /page/1, but will not be called when you switch from /page/1 to /page/2, because the DOM elements inside the /page/:number Route remain of the same type. I suggest you read React's documentation on reconciliation for a better explaination.

So you can safely put the call to your /pages service inside of the Pages component, and then in its render method display the page you need based on the activePageNumber prop.

class Pages extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      pages: null,
    };
  }

  componentDidMount() {
    axios.get('/pages')
      .then(response => {
        this.setState({
          pages: response.data
        })
      });
  }

  render() {
    const { pages } = this.state;
    const { activePageNumber } = this.props;

    return pages ? (
      <Page page={pages[activePageNumber]} />
    ) : (
      <div>loading...</div>
    );
  }
}
like image 104
steenuil Avatar answered Oct 04 '22 23:10

steenuil