Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't set state in componentWillMount

I am creating a simple chat app where I make an api call to my database via axios which returns an array of message objects. I am able to get the data when I make an axios call in componentWillMount. Then I am trying to setState to display the conversation. Here's the code:

export default class Chat extends Component {
  constructor(props){
    super(props);

    this.state = {
      messages : [],
      message : '',
    };
    this.socket = io('/api/');
    this.onSubmitMessage = this.onSubmitMessage.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
  }

  componentWillMount() {
    axios.get(`api/messages`)
      .then((result) => {
        const messages = result.data
        console.log("COMPONENT WILL Mount messages : ", messages);
        this.setState({ 
          messages: [ ...messages.content ] 
        })
  })
};

I have seen some posts concerning lifecycle functions and setting state, and it seems like I'm doing the right thing.

Again to highlight, axios call working fine, setting the state is not working. I am still seeing an empty array. Thanks in advance!

EDIT: Here is the solution to my issue specifically. It was buried in a comment, so I thought I'd leave it here..

"I discovered the issue. It was actually in how I was parsing my data. The spread operator on ...messages.content didn't work because messages.content doesn't exist. messages[i].content exists. So my fix was to spread just ...messages Then in a child component I map over the objects and parse the .content property. Thanks for the help guys!"

like image 260
Phil Avatar asked Oct 23 '16 03:10

Phil


1 Answers

In your case, your setState() won't work because you're using setState() inside an async callback

Working Fiddle: https://jsfiddle.net/xytma20g/3/

You're making an API call which is async. So, the setState will be invoke only after receiving the data. It does not do anything with componentWillMount or componentDidMount. You need to handle the empty message in your render. When you receive your data from the API, set that data to the state and component will re-render with the new state which will be reflected in your render.

Pseudo code:

export default class Chat extends Component {
  constructor(props){
    super(props);

    this.state = {
      messages : [],
      message : '',
    };
    this.socket = io('/api/');
    this.onSubmitMessage = this.onSubmitMessage.bind(this);
    this.onInputChange = this.onInputChange.bind(this);
  }

  componentWillMount() {
    axios.get(`api/messages`)
      .then((result) => {
        const messages = result.data
        console.log("COMPONENT WILL Mount messages : ", messages);
        this.setState({ 
          messages: [ ...messages.content ] 
        })
  })

  render(){
    if(this.state.messages.length === 0){
     return false //return false or a <Loader/> when you don't have anything in your message[]
    }

   //rest of your render.
  }
}; 
like image 198
Pranesh Ravi Avatar answered Sep 18 '22 17:09

Pranesh Ravi