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

IT技术 javascript ajax reactjs
2021-05-06 18:18:48

想象一下,我有一个非常简单的 React 组件,它显示了存储在其中的元素列表this.state.myList(参见下面的示例)

点击底部的“刷新”按钮会导致 React 查询后端服务器并检索它随后将显示的更新列表。与此列表的实际内容或实现无关。

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

假设 AJAX 调用(无论出于何种原因)需要几秒钟。与此同时,我很想展示一个标准的“加载”或“微调”gif,让用户知道它正在工作。

在这里这样做的最佳方法是什么?

  • 就在 AJAX 调用之前,我可以手动更新 DOM 并插入一个微调 gif,但这似乎不是“React 方法”。而且我不知道这会对 react 维护的 ReactDOM 产生什么影响。

  • isLoading如果正在加载,我可以跟踪状态并显示微调器而不是列表。但随后我需要它来render()做某事,然后立即启动另一个对 AJAX 操作的调用。

任何帮助表示赞赏。

谢谢!

3个回答

我总是解决这个问题的方法是让组件跟踪fetchInProgress其状态。

在进行提取之前,将此值设置为true; 当获取完成(成功或失败)时,您将该值设置回false.

组件的render方法然后尊重这个标志;如果标志为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>
        );
    }
});

React 模型是围绕将 UI 作为状态表示而构建的。这意味着您应该将您的状态建模为“什么是必要的数据”,而 的返回值render()正是您显示该数据的方式。

在您的情况下,您应该跟踪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>
    );
  }
});

上面@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>
    );
}