I have an Articles component which displays a list of posts. The list is paginated so that a maximum of 10 posts can display per page. There is a "Next Page" button that when clicked will update the component state and the corresponding url parameter like so:
page 1: /news
page 2: /news?page=2
page 3: /news?page=3
...and so on. The way the constructor()
and render()
methods are set up, it will read this URL parameter and display the correct posts if the user navigates directly to /news?page=3, for instance.
The issue I'm having is that the browser back and forward buttons don't seem to rerender the page. So if a user hits the "Next Page" button a few times and then hits the back button, the URL will update, but the page won't rerender. Is there a way to force it to do so?
I'm guessing there's a way to accomplish this by adding a window.history listener, but I wasn't sure if there was a recommended practice to go along with gatsby-link
.
Here is a stripped down version of the component for reference:
import React, { Component } from 'react';
import { navigateTo } from 'gatsby-link';
import getUrlParameter from '../functions/getUrlParameter';
export default class extends Component {
constructor(props) {
super(props);
/* external function, will grab value
* of ?page= url parameter if it exists */
const urlParamPage = getUrlParameter('page');
const currentPage = urlParamPage ? urlParamPage : 1;
this.state = {
currentPage
};
}
nextPage() {
const { props, state } = this;
const urlParam = state.currentPage > 1
? `?page=${state.currentPage}`
: '';
navigateTo(props.pathname + urlParam);
this.setState({currentPage: this.state.currentPage + 1});
}
render() {
const { props, state } = this;
const articles = props.articles
.slice(state.currentPage * 10, state.currentPage * 10 + 10);
return (
<div>
<ul>
{articles.map((article) => <li>{article.title}</li>)}
</ul>
<button onClick={() => this.nextPage()}>Next Page</button>
</div>
);
}
}
Your page doesn't rerender because it is actually the same page - the component is already mounted. As you are fetching your data in constructor()
, your page won't update because the constructor for a React component is called before it is mounted (source).
What you call urlParam
are just a new prop that componentWillReceiveProps(nextProps)
should receive in nextProps.location.search
.
You have to lift state up because only the root component will receive props.location
on browser back and forward buttons. The pathname
prop of your Articles component nerver changes, that is why componentWillReceiveProps
never fires here.
import React, { Component } from 'react';
import { navigateTo } from 'gatsby-link';
import Articles from '../components/Articles';
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
currentPage: 1,
data: {}, // your ext data
};
this.nextPage = this.nextPage.bind(this);
}
nextPage() {
const { currentPage } = this.state;
const { pathname } = this.props.location;
const url = `${pathname}?page=${currentPage + 1}`;
this.setState({ currentPage: currentPage + 1 });
navigateTo(url);
}
componentWillReceiveProps(nextProps) {
const { pathname, search } = nextProps.location;
const getParam = /(\d+)(?!.*\d)/;
const currentPage = search !== '' ? Number(search.match(getParam)[0]) : 1;
/* get your ext. data here */
const data = {};
this.setState({ currentPage, data });
}
render() {
const { currentPage, data } = this.state;
return (
<div>
{/* other contents here */}
<Articles
nextPage={this.nextPage}
currentPage={currentPage}
data={data}
/>
</div>
);
}
}
import React from 'react';
const Articles = ({ nextPage, currentPage }) => {
return (
<div>
<div>Page: {currentPage}</div>
<button onClick={() => nextPage()}>Next Page</button>
</div>
);
};
export default Articles;