简体   繁体   English

当我的React组件通过AJAX获取时显示加载微调器/ gif的最佳方法?

[英]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) 想象一下,我有一个非常简单的React组件,它显示了存储在this.state.myList中的元素列表(参见下面的示例)

Hitting a "Refresh" button at the bottom causes React to query the backend server and retrieve an updated list that it will then display. 点击底部的“刷新”按钮会使React查询后端服务器并检索随后将显示的更新列表。 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. 假设AJAX调用(无论出于何种原因)需要几秒钟。 In that meantime, I'd love to show a standard "loading" or "spinner" gif to let the user know it's working. 在此期间,我希望展示一个标准的“加载”或“旋转器”gif,让用户知道它正在工作。

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". 在AJAX调用之前,我可以手动更新DOM并插入一个微调器gif,但这似乎不是“React方式”。 And plus I don't know what impact that would have on the ReactDOM that react maintains. 而且我不知道对反应维持的ReactDOM会产生什么影响。

  • I could track a state for isLoading and show the spinner instead of the list if it is loading. 我可以跟踪isLoading的状态,并在加载时显示微调器而不是列表。 But then I would need it to render() something and then immediately kick off another call to an AJAX action. 但后来我需要它来render()然后立即启动另一个AJAX动作调用。

Any help appreciated. 任何帮助赞赏。

Thanks! 谢谢!

The way I always solve this is for the component to track fetchInProgress in its state. 我总是解决这个问题的方法是让组件在其状态下跟踪fetchInProgress

Before you make your fetch, you set this value to true ; 在进行提取之前,将此值设置为true ; when the fetch completes (either success or fail), you set the value back to false . 当提取完成(成功或失败)时,将值设置回false

The component's render method then honors this flag; 组件的render方法然后尊重此标志; if the flag is true , it renders a spinner instead of a dataset. 如果标志为true ,则呈现微调器而不是数据集。

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>
        );
    }
});

The React model is built around having UI as a representation of your state. React模型是围绕将UI作为您的州的表示而构建的。 That means you should model your state as "what is the necessary data" and the return value of render() is just how you display that data. 这意味着您应该将状态建模为“什么是必要的数据”,而render()的返回值就是您显示该数据的方式。

In your case, you should keep track of isLoading and in render() you conditionally display the spinner based on the value in your state. 在您的情况下,您应该跟踪isLoading并在render()根据您所在州的值有条件地显示微调器。

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

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

        {this.state.isLoading && <Spinner />}

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

small edit from @Tom's answer above. 来自@ Tom的答案的小编辑。

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>
    );
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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