Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to show a loading spinner/gif while my React component is fetching via AJAX?

Imagine I have a very simple React component that shows a list of elements that are stored in this.state.myList (see example below)

Hitting a "Refresh" button at the bottom causes React to query the backend server and retrieve an updated list that it will then display. The actual contents or implementation of this list are irrelevant.

var Foo = React.createClass({
  handleButtonClick: function() {
    return $.ajax({
      type: "POST",
      url: "/some/refresh/url",
      data: JSON.stringify({}),
      dataType: "json",
      contentType: "application/json",
      success: (function(response){
        self.setState({ myList: response.list });
      })
    });
  },

  render: function() {
    return (
      <div className="container">
        <ul>
          {
            this.state.myList.map(function(item) {
              return <li id="{item.id}">{item.name}</li>
            });
          }
        </ul>

        <input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
      </div>
    );
  }
});

Let's say the AJAX call (for whatever reason) takes a few seconds. In that meantime, I'd love to show a standard "loading" or "spinner" gif to let the user know it's working.

What's the best approach to doing that here?

  • Right before the AJAX call I could manually update the DOM and insert a spinner gif but that doesn't seem like the "React way to do it". And plus I don't know what impact that would have on the ReactDOM that react maintains.

  • I could track a state for isLoading and show the spinner instead of the list if it is loading. But then I would need it to render() something and then immediately kick off another call to an AJAX action.

Any help appreciated.

Thanks!

like image 851
user2490003 Avatar asked Feb 21 '17 05:02

user2490003


1 Answers

The way I always solve this is for the component to track fetchInProgress in its state.

Before you make your fetch, you set this value to true; when the fetch completes (either success or fail), you set the value back to false.

The component's render method then honors this flag; if the flag is true, it renders a spinner instead of a dataset.

var Foo = React.createClass({
    handleButtonClick: function() {

        // before making call, set fetch flag
        self.setState({ fetchInProgress: true });

        return $.ajax({
            type: "POST",
            url: "/some/refresh/url",
            data: JSON.stringify({}),
            dataType: "json",
            contentType: "application/json",
            success: (function(response) {
                // when updating with dataset, also reset fetch flag
                self.setState({
                    fetchInProgress: false,
                    myList: response.list
                });
            }),
            failure: ((function(reason) {
                // make sure to reset even if fetch fails!
                self.setState({
                    fetchInProgress: false
                });
            })
        });
    },

    render: function() {
        return (
            <div className="container">
                <ul>
                    {
                        this.state.fetchInProgress
                            : <Spinner />
                            : this.state.myList.map(function(item) {
                                    return <li id="{item.id}">{item.name}</li>
                                })
                    }
                </ul>

                <input type="submit" value="REFRESH LIST" onClick={this.handleButtonClick} />
            </div>
        );
    }
});
like image 56
Tom Avatar answered Oct 02 '22 07:10

Tom