AlexeiBerkov
AlexeiBerkov

Reputation: 437

ReactJS component always rerenders with external library

I have been working with ReactJS for a while. I would say I like it.

But some components life-cycles are still fogy for me. For example, I am trying to create a component who loads data only once from server.

On componentDidMount I am going not only load data but use some third-party libraries (for example jQuery components).

Below I have a very simple component with button and list. When user click the button it loads some comments into the list by AJAX request and show this list to user. When we click button again we hide this list.

So here I have questions:

  1. Why next time I click button again it triggers data loading ? Because we create new ReactElement and it executes componentDidMount.
  2. How can we improve this example to load data only once?

Please, do not suggest use display: none;)

Feel free to execute this example on jsfiddle.

var jsonData = {
    "comments" : [
        {id: 1, comment: "Comment 1"},
      {id: 2, comment: "Comment 2"},
      {id: 3, comment: "Comment 3"},
      {id: 4, comment: "Comment 4"}
    ]
};

var CommentsBox = React.createClass({

    getInitialState: function () {
        return {
            comments: [],
            loading: true
        }
    },

    componentDidMount: function () {
       var dxInstanceElement = this.refs.dxInstanceElement;

        $(dxInstanceElement).dxDataGrid(this.getDataGridOptions());
    },

    render: function () {

        var commentsNode = this.state.comments.map(function(comment){
            return (
                <p key={comment.id}>{comment.comment}</p>
            );
        });

        return (
            <div>
                {this.state.loading && <img src="http://www.ajaxload.info/images/exemples/21.gif" />}
                <div>Total count of comments: {this.state.comments.length}</div>
                <div ref="dxInstanceElement" />
            </div>
        );
    },

    getDataGridOptions: function () {
        return {

            dataSource: this.getDataGridDataSource(),

            selection: {
                mode: "multiple"
            },

            columns: [{
                dataField: "comment",
                caption: "comment"
            }]
        };
    },

    getDataGridDataSource: function () {
        var self = this,
            urlAPI = "http://jsonplaceholder.typicode.com/comments";

        var myStore = new DevExpress.data.CustomStore({
            load: function (/*loadOptions*/) {

                var d = $.Deferred();

                $.ajax({
                  type: 'POST',
                  dataType: 'json',
                  url: '/echo/json/',
                  data : { 
                    json: JSON.stringify( jsonData ),
                    delay: 3
                  },
                  success: function(data) {
                    d.resolve(data.comments, {totalCount: data.comments.length});

                    self.setState({
                      loading: false          
                    });
                  }  
              });   

              return d.promise();
            }
        });

        return new DevExpress.data.DataSource({
            store: myStore
        });
    }    
});

var DemoRouterExample = React.createClass({

    getInitialState: function () {
        return {
            submited: false
        };
    },

    onClick: function() {
        var submited = this.state.submited;

        this.setState({
            submited: !submited
        });
    },

    render: function () {
        var btnLabel = this.state.submited ? "hide list" : "load list";
        return (
            <div>                               
                <button type="button" onClick={this.onClick}>{btnLabel}</button>
                {this.state.submited && <CommentsBox />}
            </div>
        );
    }
});

ReactDOM.render(
    <DemoRouterExample />,
    document.getElementById('container')
);

Upvotes: 2

Views: 343

Answers (2)

Fran&#231;ois Richard
Fran&#231;ois Richard

Reputation: 7045

I'm not exactly sure what's the problem here but I would place the Ajax call and the corresponding state at the same level. Here maybe put your Ajax call in the parent componentdidMount then pass it as a prop to the child.

Upvotes: 0

Zhang Chao
Zhang Chao

Reputation: 757

I believe the reason is that you use {this.state.submited && <CommentsBox />} ,when submit was set to false, the CommentsBox is unmounted. The next time you set submit true, CommentsBox is mounted again.
I suggest you using <CountriesBox show={this.state.submited} /> instead. And in CommentsBox :

return (
    <div className={this.props.show ? '' : 'hide'}>
        <div>Total count of comments: {this.state.comments.length}</div>
        {commentsNode}
    </div>
);

the hide class just set to display:none;

jsfiddle

NOTE that that ajax will execute immediately you render the DemoRouterExample. Because CommentsBox is always been render.

Upvotes: 2

Related Questions