svnvav svnvav - 2 months ago 17
React JSX Question

React + AJAX component over-rendering

To fill the content of React component I use AJAX request in the body of the method componentWillMount, where I change this.state as a result of the execution of the request. The trouble is that the render method runs as before the request completion, as after the this.state change by setState. Moreover, if such component renders one, which also is used AJAX, the number of render calls grows as a power of two.

How to make the component is rendered only after obtaining the necessary data?

var News = React.createClass({
getInitialState: function () {
return {
news: [],
page: 1
};
},

componentWillMount: function () {
let xmlhttp = getXmlHttp();
xmlhttp.open("POST", "server/news.php", true);
xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlhttp.onreadystatechange=() => {
if (xmlhttp.readyState != 4) return;
clearTimeout(timeout);
if (xmlhttp.status == 200) {
this.setState({
news: eval(xmlhttp.responseText.toString())
});
} else {
handleError(xmlhttp.statusText);
}
};
xmlhttp.send('page=' + this.state.page);
var timeout = setTimeout( function(){ xmlhttp.abort(); handleError("Request time over") }, 10000);
},

render: function () {
let itemList = this.state.news.map(function(item, iterator){
return (
<NewsItem
niTitle={item[0]}
niText={item[1]}
niDate={item[2]}
niImg={item[3]}
key={"ni " + iterator}
/>
);
});
return (
<div>
{itemList}
<PageChanger page={this.state.page}/>
</div>
);
}
});

Answer

How to make the component is rendered only after obtaining the necessary data?

If your render function returns false, nothing will be rendered. (Source: the docs.)

Therefore, you can set a flag in your initial state:

getInitialState: function () {
    return { page: 1, shouldRender: false };
}

Then, in render, you can check that flag:

if (! this.state.shouldRender) return false;

In the callback passed to the XHR, you can update the flag:

this.setState({
  shouldRender: true,
  news: eval(xmlhttp.responseText.toString()),
});