1

I have a react component that based on the url should import some data and then display it in another child component. When the page first loads it loads the initial data as the component state in componentDidMount(). As for further url changes, they are handled in componentDidUpdate() export default class Info extends React.Component{

constructor(props){
    super(props);
    this.state={
        element:null
    }
}

componentDidMount(){
    switch(this.props.match.params.id){
        case 'legende': import('../data/legends.json').then((data)=>{
            this.setState({
                element:(<InfoDisplay data={data.content} />)
            })
        });break;
        case 'istorie': import('../data/history.json').then((data) => {
            this.setState({
                element: (<InfoDisplay data={data.content} />)
            })
        }); break;
    }
}

componentDidUpdate(prevProps){
    if (this.props.match.params.id !== prevProps.match.params.id)
        switch (this.props.match.params.id) {
            case 'legende': import('../data/legends.json').then((data) => {
                this.setState({
                    element: (<InfoDisplay data={data.content} />)
                })
            });
            break;
            case 'istorie': import('../data/history.json').then((data) => {
                this.setState({
                    element: (<InfoDisplay data={data.content} />)
                })
            }); break;
        default: break;
        }
}

render(){
    return (
        <div style={{backgroundImage:`url(${background})`,height:'100vh',overflowX:'hidden',backgroundSize:'cover'}}>
            <Navbar/>
            {this.state.element}
        </div>
        )
    }
}

My problem is that in spite of the state updating in the switch statement, the component won't re-render. I have no idea what's the problem with my approach. Can anyone help me out, please? Thanks!

Edit: Here is the code with shouldComponentUpdate() instead of componentDidUpdate :

import React from 'react'
import * as background from '../assets/img/background_main.png';
import Navbar from './Navbar'
import InfoDisplay from './InfoDisplay';

export default class Info extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            element: null
        }
    }

    componentDidMount() {
        switch (this.props.match.params.id) {
            case 'legende': import('../data/legends.json').then((data) => {
                this.setState({
                    element: (<InfoDisplay data={data.content} />)
                })
            }); break;
            case 'istorie': import('../data/history.json').then((data) => {
                this.setState({
                    element: (<InfoDisplay data={data.content} />)
                })
            }); break;
        }
    }

    shouldComponentUpdate(nextProps,nextState) {
        if (this.props.match.params.id !== nextProps.match.params.id && nextProps) {
            switch (nextProps.match.params.id) {
                case 'legende': import('../data/legends.json').then((data) => {
                    this.setState({
                        element: (<InfoDisplay data={data.content} />)
                    })
                }); return true;
                case 'istorie': import('../data/history.json').then((data) => {
                    this.setState({
                        element: (<InfoDisplay data={data.content} />)
                    })
                });return true;
                default: return false;
            }
        }

        return true;
    }

    render() {
        return (
            <div style={{ backgroundImage: `url(${background})`, height: '100vh', overflowX: 'hidden', backgroundSize: 'cover' }}>
                <Navbar />
                {this.state.element}
            </div>
        )
    }
}
Adrian Pascu
  • 949
  • 5
  • 20
  • 48

4 Answers4

1

You can really simplify this and make it cleaner, and this should also fix your problem:

In your render do:

return (
    <div style={{backgroundImage:`url(${background})`,height:'100vh',overflowX:'hidden',backgroundSize:'cover'}}>
        <Navbar/>
        <InfoDisplay data={this.state.data} />
    </div>
    )

Then in your componentDidMount and componentDidUpdate all you have to do is set the state data to data.content from your request:

 case 'legende': import('../data/legends.json').then((data) => {
            this.setState({
                data: data.content,
            });
        });

obviously you will need to change your constructor as well to be have data:

this.state = {data: null};

Hope this helps.

Dragomir Kolev
  • 1,088
  • 10
  • 25
  • I did just that, but for some reason it doesn't work. The state is still null and if I try to print the data variable to the console it would not print. Typed it twice, don't think is a typo – Adrian Pascu Oct 30 '18 at 19:19
1

You probably want to use shouldComponentUpdateinstead, as what you want is to do stuff before the component is re-rendered.

Check this out: https://code.likeagirl.io/understanding-react-component-life-cycle-49bf4b8674de

and this: http://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

Yoshimitsu
  • 370
  • 2
  • 9
  • Check your solution (edited the post with the updated code), but it still doesn't work. The state updates as it should, but the component is still not re-rendering – Adrian Pascu Oct 30 '18 at 19:47
  • I think I know what's the problem here. It's probably due to location changes blocking the render update. Check this: https://reacttraining.com/react-router/web/guides/dealing-with-update-blocking – Yoshimitsu Oct 30 '18 at 20:15
  • Not on my computer right now so can't check. If I understand correctly, passing the location as a prop to the component that I want to render should fix my problem? – Adrian Pascu Oct 30 '18 at 20:28
  • 1
    I figured it out. I forced the re-rendering by first setting the element in state to null and immediately after setting it to the JSX node in `shouldComponentUpdate()`. Thanks for the help ! – Adrian Pascu Oct 31 '18 at 07:22
0

componentDidUpdate() will be invoked if a re-rendering is complete => In fact like you said there is no re-rendering because you never invoke any setState Method.

I'm not very sure if a state can hold a JSX content.

Maybe try it like this:

this.setState({
    element: data.content
})

==

<div style={{backgroundImage:`url(${background})`,height:'100vh',overflowX:'hidden',backgroundSize:'cover'}}>
    <Navbar/>
    <InfoDisplay data={this.state.element} />
</div>

hope you find your bug with this information

Sandro Schaurer
  • 416
  • 4
  • 13
-1

if you want to re-render on any update then you should use

componentWillReceiveProps(nextProps){
  this.setState({element:nextProps.data})
}
Amit Chauhan
  • 1,810
  • 3
  • 17
  • 25