bsizzle
bsizzle

Reputation: 417

ReactJS rerender after AJAX call

Learning react and whilst I understand the life cycle of a component, I don't understand that whilst my JSON call is successful it does not render the list of items I've pulled initially. It does render when I add a new item to the list, but that requires me to click the edit button and add a new element.

var InterBox = React.createClass({
    getInitialState: function(){
        return {
            "content" : [],
            "editing" : false,
            "pulled" : false
        }
    },
    componentDidMount: function(){
        var url = "http://jsonplaceholder.typicode.com/posts";
        var tempdata = this.state.content;
        $.getJSON(url, function(data){
            for (var i = 0; i < data.length; i++) {
                var element = data[i];
                tempdata.push(element.body);
                console.log(tempdata);
            }
        });
        this.setState({"content":tempdata});
        this.forceUpdate();
    },
    addContent: function(e){
        var currentContent = this.state.content;
        currentContent.push(this.refs.content.value);
        this.refs.content.value = "";
        this.setState({"content" : currentContent, "editing" : false});
        this.render();
    },
    changeMode: function(e){
        this.setState({"editing": true});
        this.render();
    },
    render: function(){
        if (this.state.editing)
            return(
                <div>
                    <h1>{this.props.title}</h1>
                    <input type="text" ref="content"/>
                    <button onClick={this.addContent}>Add</button>
                </div>
            );
        else{
            var items = [];
            for (var i = 0; i < this.state.content.length; i++) {
                var element = this.state.content[i];
                items.push(<li>{element}</li>);
            }
            return(
                <div>
                    <h1>{this.props.title}</h1>
                    <button onClick={this.changeMode}>Edit</button>
                    <ul>
                        {items}
                    </ul>
                </div>
            );
        }
    }
});
ReactDOM.render(
    <InterBox title="Hello"></InterBox>,
    document.getElementById('main')
);

This is the initial output

The Output

This what it should look like

enter image description here

Upvotes: 2

Views: 3213

Answers (3)

Kokovin Vladislav
Kokovin Vladislav

Reputation: 10421

1.) Since $.getJSON func is async, therefore you should wait response, and after that invoke setState in callback.

  let {content} = this.state;
  $.getJSON(url,data=>{
         let result = data.map(e=>e.body);
         this.setState({content:[...content, ...result]}); 
   });

2.) You don't need forceUpdate() and render(), because setState invoke rendering component for you.

3.) You shouldn't mutate your state by push().It will causing problems and bugs. Instead you may use spread operator or concat, etc

//concat returns new array
let {content} = this.state;
this.setState({content:content.concat(result)}); 

//spread operator
let {content} = this.state;
this.setState({content:[...content, ...result]}); 

4.) If you don't want to use arrow func in callback, you should use bind(this), otherwise you will have undefined for setState in callback

 let {content} = this.state;
 $.getJSON(url,function(data){
              let result = data.map(e=>e.body);
              this.setState({content:content.concat(result)}); 
         }.bind(this));

Upvotes: 5

MattDuFeu
MattDuFeu

Reputation: 1655

There is no need for a this.forceUpdate() or this.render() call. The screen will be refreshed, i.e. render() called by React after this.setState().

The reason the data isn't being shown is because you're calling this.setState() immediately after calling $.getJSON() and not in the success callback of getJSON().

This should work:

$.getJSON(url, function(data){
    for (var i = 0; i < data.length; i++) {
        var element = data[i];
        tempdata.push(element.body);
        console.log(tempdata);
        this.setState({"content":tempdata});
    }
});

Upvotes: 0

Piyush.kapoor
Piyush.kapoor

Reputation: 6803

  1. Dont call render function manually i.e. supposed to call by react. 2.You were setting setState outside your AJAX call response.

    var InterBox = React.createClass({
        getInitialState: function(){
            return {
                "content" : [],
                "editing" : false,
                "pulled" : false
            }
        },
        componentDidMount: function(){
            var url = "http://jsonplaceholder.typicode.com/posts";
            var tempdata = this.state.content;
            $.getJSON(url, function(data){
                for (var i = 0; i < data.length; i++) {
                    var element = data[i];
                    tempdata.push(element.body);
                    console.log(tempdata);
                }
                        this.setState({"content":tempdata});
            });
    
        },
        addContent: function(e){
            var currentContent = this.state.content;
            currentContent.push(this.refs.content.value);
            this.refs.content.value = "";
            this.setState({"content" : currentContent, "editing" : false});
        },
    changeMode: function(e){
        this.setState({"editing": true});
    },
    render: function(){
        if (this.state.editing)
            return(
                <div>
                    <h1>{this.props.title}</h1>
                    <input type="text" ref="content"/>
                    <button onClick={this.addContent}>Add</button>
                </div>
            );
        else{
            var items = [];
            for (var i = 0; i < this.state.content.length; i++) {
                var element = this.state.content[i];
                items.push(<li>{element}</li>);
            }
            return(
                <div>
                    <h1>{this.props.title}</h1>
                    <button onClick={this.changeMode}>Edit</button>
                    <ul>
                        {items}
                    </ul>
                </div>
            );
        }
    }
    

    });

    ReactDOM.render(
        <InterBox title="Hello"></InterBox>,
        document.getElementById('main')
    );
    

Upvotes: 0

Related Questions