0

I am playing around with reactjs and here is what I am doing: Am working on retrieving data from an API and displaying the data Using axios to make a call to the API. I know that axios calls are asynchronous and promise based.

My understanding is that in react 'setState' can only be called after the component has mounted ( which happens in componentDidMount ). So the call to the API is made in 'componentDidMount'

what I am not clear is : why does this work and why do I get the data displayed ?

I was reading here that 'render' gets fired before 'componentDidMount' completes

So given the fact that 'render' is before 'componentDidMount' I am confused why the data loads and shows up fine ?

I had also looked at this site where it makes use of some flags to determine if data has loaded or not and then decides whether to show a spinner or not I did not have to do any such thing as well So while it does display data , I am sure there is more to this ...

Here is the code:

class StudentsPage extends React.Component{
constructor(props){
    super(props);

    this.state = {
        isLoaded: false,
        studentListArray: []
    }
}


 componentDidMount(){
       /** now get results from api call and need to set the result to the state of this class */

       /** setting state MUST happen using setState and no other way - this is important */
       /** NOTE below call to setState will ONLY modify the attribute 'studentListArray' of state
        *  if there were other attributes / properties in state they would not get impacted / modified
        *  so setState is more like setters for individual properties 
        *  you can change multiple properties in one setter OR call setState for each property you want to change !
        */

     /** define the endpoint to be called */
    const baseUrl = process.env.REACT_APP_API_URL + "/studentList/";
    axios.get(baseUrl).then(({data}) => {
        this.setState(
            { studentListArray: data }
       );            
    })

  }
render(){ 
    return(
        <React.Fragment>
        <table className="table">
            <thead>
                <tr>
                    <th>Sudent ID</th>
                    <th>Student subject</th>
                </tr>
            </thead>

            <tbody>
                {this.state.studentListArray.map((student) => {
                    return (<tr>
                        <td>{student.id}</td>
                        <td>{student.subject}</td>
                    </tr>);

                })}
            </tbody>
        </table>
        </React.Fragment>
    );
}
}
satish marathe
  • 1,073
  • 5
  • 18
  • 37
  • 2
    This works exactly as intended. I suggest put some `console.log`s in your `render` method to see what `this.state.studentListArray` is initially (empty array), then after you call `this.setState`. The reason why it may appear in the way you're describing is b/c initially your array is empty, so it doesn't break nor renders anything, and as soon as you get your response and update `studentListArray`, it will immediately re-render. The response from the server is instant, hence the quick re-render. – goto May 24 '20 at 23:36
  • Also, one way you can prove that everything works as expected, change your initial value for `this.state.studentListArray` to `null` and watch your code break because `axios.get` call hasn't finished fetching and updating the state prior to the first render. – goto May 24 '20 at 23:40
  • 1
    WHY IT WORKS? ... async result modifies state (using setState) - **any `state` or `props` change is a reason of rerendering** (updating view) - this way you can say that using `setState` (with different data then current) forces rerendering (displaying fresh view with new data/state) – xadm May 25 '20 at 00:11
  • thanks , I guess what I missed was that 'setState' will again call 'render' and I was also wrong about the order of methods executing ( componentDidMount and then render ( my assumption ) Vs the actual order render >> componentDidMOunt >> render ( if state changes ) – satish marathe May 26 '20 at 22:40
  • I will post a separate question for a variation of the same code but which fails – satish marathe May 26 '20 at 22:40

1 Answers1

1

When a class-based component is mounted, it's lifecycle methods are executed in the following order:

  1. constructor()
  2. static getDerivedStateFromProps()
  3. render()
  4. componentDidMount()

In your case, after the StudentPage component is mounted, it immediately attempts to fetch the data which then triggers a component update/re-render.

When the component re-renders, it calls the following lifecycle methods in the following order:

  1. static getDerivedStateFromProps
  2. shouldComponentUpdate()
  3. render()
  4. getSnapShotBeforeUpdate()
  5. componentDidUpdate()

You'll be able to more clearly visualise this if you place a break point or a simple console.log() in render(), componentDidMount() and componentDidUpdate().

This is pure speculation from my side, but it is possible that the (I'm assuming local) server you're calling is responding with the data you're expecting much quicker, so it seems like the data is readily available on first mount.

If you wish to find out more about React's lifecycle methods then don't hesitate to refer to the official React documentation.

V. Dimitrov
  • 162
  • 1
  • 12
  • thanks , yes what I was missing was that render does get called again when state changes and I had the order of method execution wrong in my mind ( componentDidMount and then render ) – satish marathe May 26 '20 at 22:42