So I have an interesting case of using react with Ajax call.
To put it in the context, I have an accordion with 3 tabs. After initializing Accordion react component, I have the first tab initially open, the rest is closed. Every tab has in it's body so called DictionaryCall component which looks like this:
return class DictionaryCall extends React.Component {
constructor (props) {
super();
this.state = {
word: '',
data: [],
error: false,
nodata: false,
initialLoaded: props.load
}
}
componentDidMount () {
if(this.state.initialLoaded){
this.callAjax();
}
}
componentWillReceiveProps (nextProps) {
if(nextProps.load){
this.setState({initialLoaded: true});
this.callAjax();
}
}
callAjax () {
$.ajax({
url: this.props.url,
dataType: 'json',
catche: false,
method: 'POST',
data: {word: this.props.word},
success: function(data){
if(!data.length){
this.setState({nodata: true});
} else {
this.setState({data: data});
}
}.bind(this),
error: function(xhr, status, error){
console.log(this.props.url, status, error.toString());
this.setState({error: true});
}.bind(this)
});
}
render () {
let body,
error = this.state.error;
if(this.state.nodata){
body = <div>No definition found</div>
} else {
body = <DataTab data={this.state.data} title={this.props.word}/>
}
return (
<div className="dictionary-call">
{body}
{error ? <ServerError onClick={this.callAjax.bind(this)}/> : null}
</div>
)
}
};
Fist of all according to React docs setting initial state with props is an anti-pattern, until you specify explicitly it's only for component initialization.
So as it can be seen in the constructor, I'm setting initialLoaded
state with props.load
. I'm passing props.load
as true
only to the first Accordion tab as I want it to be loaded initially.
componentDidMount
method is called and checks initialLoaded
state. If it's set to true
it simply calls ajax and re-render the component.
Now the tricky bit. The componentWillReceiveProps
method. I'm expecting, the component will receive nextProps.load
when user clicks on the Accordion tab to open it. Then props.load
is passed to the component with true
value.
My question is, is componentWillReceiveProps
a good place to call this.callAjax()
? It seems a bit pointless to create the initalLoaded
state in this case. Shouldn't I simply base on props.load
instead and call shouldComponentUpdate
?
Or maybe I should stick with initalLoaded
state and use componentWillUpdate
or componentDidUpdate
.
Bear in mind I want to call ajax call only once when the accordion tab gets opened for the first time.
Thanks!
According to official React docs, the recommended place to do Ajax requests is in componentDidMount which is a lifecycle method that runs after the React component has been mounted to the DOM.
Where in the component lifecycle should I make an AJAX call? You should populate data with AJAX calls in the componentDidMount lifecycle method. This is so you can use setState to update your component when the data is retrieved.
2. How many times componentDidMount is called? React components call componentDidMount only once by default. You can run the component multiple times if you delete the component or change the props or state.
So after a bit of research I want to answer my own question. Hope it helps someone.
The solutions is very simple and clean (in my opinion).
componentDidMount () {
if(this.state.initialLoaded){
this.callAjax();
}
}
componentWillReceiveProps (nextProps) {
if(nextProps.load){
this.setState({initialLoaded: true});
}
}
componentWillUpdate (nextProps, nextState) {
if(this.state.initialLoaded !== nextState.initialLoaded) {
this.callAjax();
}
}
This code applies to all instances of the component, no matter if it is a child of the first accordion tab (initially open) or the rest of tabs (initially closed).
In componentDidMount
method, I'm checking if component should make an Ajax call. If during the initialization the this.props.open
has set the this.state.initialLoaded
state to true, than the ajax call is made. Otherwise of course not.
Now, when user clicks other tab, the component is expecting props in componentWillReceiveProps
. Here I'm setting state only if nextProps.load
is true, as I want to potentially load the data only if load
field is true.
Lastly, if the condition in componentWillReceiveProps
has been met, I'm checking if this.state.initialLoaded
has changed. As it could be changed only when nextProp.load
is true, it prevents from calling Ajax requests too many times (when state changes from true to false).
This way, I'm calling the Ajax request only when the tab is opened for the first time, or when it is initially opened.
That simple!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With