简体   繁体   中英

React component with ajax call - life-cycle

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM