I can't seem to get the fetch run again after it ran once no matter what I do. It runs once and loads the page according to the data received but when I navigate to another URL through a link (without reloading the entire page) it doesn't change anything. Not even the state. It doesn't do any different when fetch throws an error too.
Help is appreciated to get it working. CompanyContextProvider provides some fetched data if not available on the localStorage. I'm using react-router-dom
v6
My app.js
looks like this,
<CompanyContextProvider>
<BrowserRouter basename={'unicorn'}>
<Routes>
<Route path={'/'} element={<Home companies={this.state.companies} />} />
<Route path="/:page" element={<Page />} />
Page.js
looks like this,
import React, {Component} from "react";
import Header from "../../common/header/Header";
import {withRouter} from "../../../helpers/Helpers";
import NotFound from "../../common/NotFound";
class Page extends Component{
state = {
isLoading:false,
page:{
pageContent:{},
vacancies:[]
},
error:0,
path:''
}
componentDidMount() {
this.setState({isLoading:true})
const base_url = process.env.REACT_APP_API_BASE
const location = this.props.params.page;
this.setState({
path:this.props.location
})
fetch(base_url + 'api/page/'+location)
.then(res => res.json())
.then(
(result) => {
this.setState({
page:result,
isLoading:false,
error:0
})
}, (error) => {
//console.log(error)
this.setState({
isLoading:false,
error:404,
page:{
pageContent:{},
vacancies:[]
},
})
}
)
}
render() {
function createMarkup(content) {
return {__html: content};
}
return (
<>
<Header />
{
this.state.error > 0
? <NotFound />
: !this.state.isLoading
? <div key={'content'} className={'container mx-auto mt-8'}>
<h1 className={'text-4xl my-4'}>{this.state.page.pageContent.title}</h1>
<p dangerouslySetInnerHTML={createMarkup(this.state.page.pageContent.content)} />
</div>
: <div>Loading...</div>
}
</>
)
}
}
export default withRouter(Page)
withRouter()
looks like this,
export function withRouter( Child ) {
return ( props ) => {
const location = useLocation();
const params = useParams();
const history = useNavigate();
return <Child { ...props } params={params} location={ location } history={history} />;
}
}
componentDidMount
is only called once when the component mounts. If you have logic that needs to run again later when some condition changes then you will need to also implement the componentDidUpdate
lifecycle method.
Abstract the common logic into a utility function that can be called from either lifecycle method.
const base_url = process.env.REACT_APP_API_BASE;
...
fetchPage = () => {
const location = this.props.params.page;
this.setState({
isLoading: true
path: this.props.location
});
fetch(base_url + 'api/page/' + location)
.then(res => res.json())
.then((result) => {
this.setState({
page: result,
error: 0
})
})
.catch((error) => {
//console.log(error)
this.setState({
error: 404,
page: {
pageContent: {},
vacancies: []
},
});
})
.finally(() => {
this.setState({
isLoading: false,
});
});
}
componentDidMount() {
this.fetchPage();
}
componentDidUpdate(prevProps) {
if (prevProps.params.page !== this.props.params.page) {
this.fetchPage();
}
}