React not loading state properly on mount during axios call

Viewed 517

Basically I am making a basic react App that is grabbing some data from a DB and I am stuck on the basic setup.

My intention is to have my state contain the response from my server querying my databse.

My response is 100% working and sending the data back as expected from the axios call, however the state is never getting update.

EDIT : I am attempting to pass the movies down the chain to a Component called MovieList, I have provided the code for that as well.

App.jsx

import React from 'react';
import axios from 'axios';

import MovieList from './MovieList.jsx';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {allMovies: []};
   
  }

  componentDidMount() {
    var that = this;
    axios.get('http://localhost:3000/api/movies')
      .then( res => {
        that.setState({allMovies: res.data});
      })
      .catch( err => {
        console.log(`Err @ [ App.jsx - componentDidMount ] ::: ${err}`);
      });
  }

  render() {
    return (
      <div>
        <h1>Hello World!</h1>
        <MovieList movies={this.state.allMovies} />
      </div>
    )
  }
}

MovieList.jsx

     import React from 'react';

class MovieList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
        console.log(this.props); //EMPTY OBJECT MOVIES DIDN'T GET INTO PROPS
    }

    render() {
        return (
            <div>
                <h1>Test</h1>
            </div>
        )
    }
}

export default MovieList;

NOTE : I also logged the props on mount and attempted to render them and they were empty.

Basically if I try to pass down this.state.allMovies or console.log it, its always just the initial empty array.

Maybe I don't understand how async setting the state can be done? I took a similar approach on my last school project and it seemed to work fine.

2 Answers

You don't await the axios promise to resolve, so you simply are logging what the state is when the component mounts. Use one of the following to log updated react state.

Use componentDidUpdate to log the updated state.

componentDidMount() {
  console.log(this.state);
}

Use the setState callback function to log the state

componentDidMount() {
  var that = this;
  axios.get('http://localhost:3000/api/movies')
    .then( res => {
      console.log(res.data);
      that.setState(
        { allMovies: [res.data] },
        () => console.log(this.state), // <-- setState callback function
      );
    })
    .catch( err => {
      console.log(`Err @ [ App.jsx - componentDidMount ] ::: ${err}`);
    });
}

You'll never see it in your constructor, because when your component is instantiated, it's done so with an empty array.

You will see it if you do a console.log(this.props) in componentDidUpdate or render however.

This is because when App is mounted, your component passes a movies prop of [] to MovieList. After the movies return from the server (and you update the state of App), App will render again and pass the array returned from the server, causing your MovieList component to render again. It's constructor won't be called, because it's already instantiated, but MovieList will call componentDidUpdate and render again.

class MovieList extends React.Component {
    constructor(props) {
        super(props);
        this.state = {};
    }

    render() {
        console.log(this.props); // you'll see this get logged twice - once with an empty array for movies and once with the movies returned from the server.
      
        return (
            <div>
                <h1>Test</h1>
            </div>
        )
    }
}
Related