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!
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!
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.