29

I am trying to make REST call from a react component and render the returned JSON data into the DOM

Here is my component

import React from 'react';

export default class ItemLister extends React.Component {
    constructor() {
        super();
        this.state = { items: [] };
    }

    componentDidMount() {
        fetch(`http://api/call`) 
            .then(result=> {
                this.setState({items:result.json()});
            });
    }

    render() {        
        return(
           WHAT SHOULD THIS RETURN?
        );
    }

In order to bind the returned json in a DOM?

user_mda
  • 18,148
  • 27
  • 82
  • 145

4 Answers4

45

There are a couple of errors in your code. The one that's probably tripping you up is the this.setState({items:result.json()})

Fetch's .json() method returns a promise, so it will need to be dealt with as async.

fetch(`http://jsonplaceholder.typicode.com/posts`)
.then(result=>result.json())
.then(items=>this.setState({items}))

I don't know why .json() returns a promise (if anyone can shed light, I'm interested).

For the render function, here you go...

<ul>
   {this.state.items.map(item=><li key={item.id}>{item.body}</li>)}
</ul>

Don't forget the unique key!

For the other answer, there's no need to bind map.

Here it is working...

http://jsfiddle.net/weqm8q5w/6/

D. Walsh
  • 1,963
  • 1
  • 21
  • 23
  • The fetch() function returns a Promise because of it's asynchronous nature. Ir's result will be available only after the http request is completed, so a Promise is the best solution to handle it. – JSchirrmacher Mar 12 '17 at 14:07
  • 4
    @JoachimSchirrmacher I'm referring to why result.json() returns a promise. Nothing about it requires execution outside of the current execution context. – D. Walsh Mar 13 '17 at 01:26
  • 9
    .json() returns a promise because JSON.parse() is pretty slow on a large result, and because it can throw an exception if it fails. – dkloke Apr 20 '17 at 10:21
  • Does .json() return a promise because it's reading from the body's stream which might not be downloaded yet? – jimmyfever Jun 16 '18 at 03:09
3

You can try this for your render method:

render() {
    var resultNodes = this.state.items.map(function(result, index) {
        return (
            <div>result<div/>
        );
    }.bind(this));
    return (
        <div>
            {resultNodes}
        </div>
    );
}

and don't forget to use .bind(this) for your fetch(...).then(), I don't think it could work without...

Mickaël R.
  • 344
  • 2
  • 7
1

Fetch methods will return a Promise that makes it straightforward to write code that works in an asynchronous manner:

In your case:

componentDidMount(){
  fetch('http://api/call')      // returns a promise object
    .then( result => result.json()) // still returns a promise object, U need to chain it again
    .then( items => this.setState({items}));
}

result.json() returns a promise, because this it works on a response stream and we need to process entire response first in order to work.

mohdasha
  • 311
  • 1
  • 7
0

Use the following instead. It will work: (You can also check the data if loaded in the console)


 constructor(props) {
        super(props);
        this.state = {
            items: []
        }
    }

 componentDidMount() {
        fetch('http://api/call')
            .then(Response => Response.json())
            .then(res => {
                console.log(res);
                this.setState({
                    items: res,
                });
            })
            .catch(error => {
                console.log(error)
            })
    }

Then use the result stored in state during render to display as required.

Hirak JD
  • 181
  • 2
  • 7