Search code examples
javascriptreactjsfetchreact-router-domreact-context

Fetch doesn't trigger when routed to another page


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} />;
    }
}

Solution

  • 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();
      }
    }