Search code examples
reactjstypescriptreact-routeraxiosapi-design

How to dynamically update content based on route?


I asked a question not too long ago and got an answer that helped me greatly, but now i'm stumped again. I'm kinda new to React, so still learning a few tips and tricks. I've got a page with a table full of contents fetched from an API based on the year. In the sidebar i've got a list of the possible years. I was stuck originally based on that i only used ComponentDidMount, but got help that I needed an update function when a link was clicked. The problem i'm now having is that I need to press the link twice for the content to update. I can see in the browser that the route has changed, but not the content.

I've tried searching Google, but couldn't find anything. Also tried to use React Router's this.props.history.push() since the API bases itself on the this.props.match.params.yearId and this.props.location.search which equals Year?year=2019(or the clicked year).

class YearlyTable extends React.Component {
    state = {
        yearlyTable: [],
        isLoading: false,
    }

    componentDidMount() {
        this.setState({ isLoading: true });
        axios.get(
            `http://localhost/YearlyTable/${this.props.match.params.yearId}${this.props.location.search}`,
            { withCredentials: true }
        ).then(res => {
            const yearlyTable = res.data;
            this.setState({ yearlyTable, isLoading: false });
        }).catch((error) => {
            console.log(error);
        });
    }

    updateData(){
        this.setState({ isLoading: true });
        axios.get(
           `http://localhost/YearlyTable/${this.props.match.params.yearId}${this.props.location.search}`,
           { withCredentials: true }
       ).then(res => {
           const yearlyTable = res.data;
           this.setState({ yearlyTable, isLoading: false });
       }).catch((error) => {
           console.log(error);
       });
    }

    render() {
        if (this.state.isLoading) {
            return (
                <Box style={{textAlign: 'center'}}>
                    <CircularProgress color="primary" />
                </Box>
            );
        }

        // Check what API returns
        console.log(this.state.yearlyTable);

        return (
            // Removed for simplicity
            {this.state.yearlyTable && <ListTable title={this.state.yearlyTable.Title} data={this.state.yearlyTable} />}

            // Removed for simplicity (Sidebar)
            // Example of link(MaterialUI, with RouterLink as React-Router-Dom's Link)
            <Link component={RouterLink} to={'/YearlyTable/Year?year=2018'} onClick={this.updateData.bind(this)}>2018</Link>
        );
    }
}
export default withRouter(YearlyTable);

The desired outcome would be for the information to update dynamically without having to press the button twice, since this is a horrible user-experience.


Solution

  • use componentDidUpdate lifecycle method

    componentDidUpdate(prevProps, prevState) {
        if (prevProps.location.search != this.props.location.search && this.state.isLoading != true) {
            this.setState({ isLoading: true });
            axios.get(
                `http://localhost/YearlyTable/${this.props.match.params.yearId}${this.props.location.search}`,
                { withCredentials: true }
            ).then(res => {
                const yearlyTable = res.data;
                this.setState({ yearlyTable, isLoading: false });
            }).catch((error) => {
                console.log(error);
            });
        }
    }