panditmonium
panditmonium

Reputation: 19

Why does my state variable in ReactJS not render and why is it undefined?

Beginner to React and JavaScript, so forgive me if this is a simple fix.

I'm fetching data from a server and intend on parsing the response and storing elements of the response in an array which is a state variable, as I want to use variations in the state to update the page accordingly.

The issue I am having is that while using fetch(), I am able to retrieve the data from the server, but apparently I am unable to use setState() properly to assign the data to the variable. I've read that setState() is asynchronous, so your state variable might not be updated right away, and in that case I wonder if I can render the application whenever my state variable (array) is updated.

Below is my code:

import React, { Component } from 'react';
import './App.css';

class App extends Component{
  constructor(props)  {
    super(props);
    this.state = { 
      apiResponse: []
    }; 
    this.getData = this.getData.bind(this);
  }
  componentDidMount() {
    this.getData();
  }

  getData = () => {
    fetch('http://localhost:5000')
      .then(results => results.json())
      .then(console.log)
      .then(results => this.setState({apiResponse: results}, () => {
        console.log('data in state', this.state.apiResponse)
      }))
      .catch(err => console.error(err));
  }
  render()  {
    return (
      <div className="App">
        <h1>Display</h1>
        <h1>{this.state.apiResponse}</h1>
        <h1>Does this work?</h1>
      </div>
    );
  }
}

export default App;

The first console.log that I run after fetch returns the appropriate JSON object, however the next console.log after returns an undefined, and {this.state.apiResponse} does not render anything.

Upvotes: 0

Views: 1278

Answers (5)

Siju Samson
Siju Samson

Reputation: 517

    import React, { Component } from 'react';
    import './App.css';
    
    class App extends Component{
      constructor(props)  {
        super(props);
        this.state = { 
          apiResponse: []
        }; 
        this.getData = this.getData.bind(this);
      }
      componentDidMount() {
        this.getData();
      }
    
      getData = () => {
        fetch('http://localhost:5000')
          .then(results => results.json())
          .then(console.log)
          .then(results => this.setState({apiResponse: results}))
          .catch(err => console.error(err));
      }
      render()  {
        return (
          <div className="App">
            <h1>Display</h1>
            <h1>{this.state.apiResponse && 
         this.state.apiResponse.recordSets.map((singleRecord, index)=>           
  singleRecord.map(singleElement => <span> 
              {singleElement.FactoryName}</span>))}</h1>
                <h1>Does this work?</h1>
              </div>
            );
      }
    }
    
    export default App;

Upvotes: 0

shiponcs
shiponcs

Reputation: 1687

It's problem in your Chaining, Here's a answer:

The then method returns a Promise which allows for method chaining.

If the function passed as handler to then returns a Promise, an equivalent Promise will be exposed to the subsequent then in the method chain. - MDN

...
      .then(console.log) // here you're not exposing any response
...
  }

A remedy could be:

...
getData = () => {
    fetch('http://localhost:5000')
      .then(results => {console.log(results); return results.json()})
      .then(results => this.setState({apiResponse: results}, () => {
        console.log('data in state', this.state.apiResponse)
      }))
      .catch(err => console.error(err));
  }
...

Upvotes: 0

Yves Kipondo
Yves Kipondo

Reputation: 5623

The problem is when you pass as callback to the then consol.log function and after what you continue with the then chaining and expect to have access to the result, what you should do by the way is call directly the then chain in which you set the state before call to console.log and then return results in that callback like this

fetch('http://localhost:5000')
  .then(results => results.json()) 
  .then(results => {
      this.setState({apiResponse: results}, () => {
          console.log('data in state', this.state.apiResponse)
      });
      return result;
   })
   .then(console.log);

Or just replace it with a normal function like this

fetch('http://localhost:5000')
  .then(results => results.json())
  .then((results) => {
      console.log(results);
      return results;
  })
  .then(results => {
      this.setState({apiResponse: results}, () => {
          console.log('data in state', this.state.apiResponse)
      });
      return result;
   });

Upvotes: 1

VladislavChudak
VladislavChudak

Reputation: 36

whenever you resolve a Promise using .then() you want to pass resolve to the next callback. .then(console.log) breaks that. You don't have the results in your next callback. Read more about that here .

Upvotes: 0

Apostolos
Apostolos

Reputation: 10498

You are handling the json object with then method but then you don't return something in order for the next then to have as input.

If you remove this line

.then(console.log)

then it should work.

Upvotes: 1

Related Questions