【问题标题】:Display spinner during AJAX call when using fetch API使用 fetch API 时在 AJAX 调用期间显示微调器
【发布时间】:2017-10-03 04:09:00
【问题描述】:

在我的项目中,我正在迁移到 React,因此不加载 JQuery。由于我不再使用 JQuery,因此对于 AJAX 调用,我使用的是 fetch。使用 JQuery,我可以挂钩 AJAX 调用的开始和结束,因此很容易将光标更改为微调器。我在 fetch 中找不到类似的钩子。除了在每个单独的 AJAX 调用中更改它之外,还有其他方法吗?

大量谷歌搜索只是不断寻找关于... JQuery 的答案。

【问题讨论】:

  • 全局挂钩在 jQuery 中是个坏主意,这并没有停止。为您的应用程序正在执行的每个单独请求执行微调器。
  • @Bergi 为什么全局挂钩是个坏主意?
  • 因为您可能正在使用一个库,该库执行自己的 fetch 请求,并且您不想拦截它们。或者您可能希望不再拦截所有您自己的请求。为了提高可维护性,只需编写自己的包装函数来处理微调器,然后在需要使用微调器发出请求的任何地方调用它。

标签: javascript ajax fetch-api


【解决方案1】:

你去吧,我认为代码是不言自明的:

// Store a copy of the fetch function
var _oldFetch = fetch; 

// Create our new version of the fetch function
window.fetch = function(){

    // Create hooks
    var fetchStart = new Event( 'fetchStart', { 'view': document, 'bubbles': true, 'cancelable': false } );
    var fetchEnd = new Event( 'fetchEnd', { 'view': document, 'bubbles': true, 'cancelable': false } );

    // Pass the supplied arguments to the real fetch function
    var fetchCall = _oldFetch.apply(this, arguments);

    // Trigger the fetchStart event
    document.dispatchEvent(fetchStart);

    fetchCall.then(function(){
        // Trigger the fetchEnd event
        document.dispatchEvent(fetchEnd);
    }).catch(function(){
        // Trigger the fetchEnd event
        document.dispatchEvent(fetchEnd);
    });

    return fetchCall;
};

document.addEventListener('fetchStart', function() {
    console.log("Show spinner");
});

document.addEventListener('fetchEnd', function() {
    console.log("Hide spinner");
});

这是一个活生生的例子:https://jsfiddle.net/4fxfcp7g/4/

【讨论】:

  • 请注意 fetchEnd 在整个响应正文加载之前触发
  • 这正是我想到的那种东西,给你一箱啤酒!谢谢!
  • 很高兴它帮助了你!只要确保使用我的 sn-p 的最新编辑。
  • @EtienneMartin 最新的似乎被破坏了......对休息端点的调用以错误“TypeError:已读”终止
  • 你是对的,我的错。我恢复到以前的版本。
【解决方案2】:

在 thenable 中获取,可以在 then() 中添加 spinner stop 函数,在收到响应后调用。并将其包装在另一个函数中,用于 headers 和 hooking。

function ajax(someUrl, method) {
    var myHeaders = new Headers();

    var myInit = { 
        method: method,
        headers: myHeaders
    };
    showSpinner();

    return fetch(someUrl, myInit).then(function(response) {
        //... do some with response
        hideSpinner();

        return response;
    }).catch(function(error) {
        //... tell the user the request failed and hide the spinner
        hideSpinner();

        return error;
    });
}

ajax('example.com').then(function(response) {
    // ...
}).catch(function(error) {
    // show error
});

【讨论】:

  • 是的,谢谢,我知道,但这涉及在每个 AJAX 调用中放置 showSpinner() 和 hideSpinner()...
  • 我认为你误解了这个问题,OP 想要挂钩每个 fetch 调用的开始和结束。
  • @JimArcher 确实......也许将它包装在某个函数中?并调用该包装器而不是获取?我已经改变了例子。
  • 这是正确的想法,但@EtienneMartin 成功了!
【解决方案3】:

我发现使用手动设置“加载”标志的主要问题 true / false 是很容易错误地跳过设置为 false 标志并且您的应用程序想要挂起),另一方面我不想在并行调用多个 ajax 请求时搞砸,这甚至当您想要跟踪其中一些时会更加困难,但在其他情况下您想要发出静默请求。

我创建了一个名为 react-promise-tracker 的微型库,以更自动的方式处理此问题。它是如何工作的

安装承诺跟踪器

npm install react-promise-tracker --save

实现一个简单的加载指示器组件(你可以为其添加你需要的样式),并用 promiseTrackerHoc

包裹它
import { promiseTrackerHoc } from 'react-promise-tracker';

const InnerLoadingIndicator = props => (
   props.trackedPromiseInProgress &&
   <h1>Hey some async call in progress ! Add your styling here</h1>
);

export const LoadingIndicator = promiseTrackerHoc(InnerLoadingIndicator);

在您的应用程序根组件中实例化它:

render(
  <div>
    <App />
    <LoadingIndicator/>
  </div>,
  document.getElementById("root")
);

当你想跟踪一个 fetch 或任何基于 promise 的调用(包括 async/await)时,只需用 react-promise-tracker 函数 trackPromise 包装它:

import { trackPromise } from 'react-promise-tracker';

export class MyComponent extends Component {
  componentWillMount() {
    trackPromise(
      userAPI.fetchUsers()
        .then((users) => {
          this.setState({
            users,
          })
        })
    );
  }

  // ...
}

更多资源: - 分步教程(包括添加漂亮的微调器):https://www.basefactor.com/react-how-to-display-a-loading-indicator-on-fetch-calls

【讨论】:

    【解决方案4】:

    fetch 最初发生时你可以做什么,你setState 加载为true。然后,一旦 fetch 完成,你可以setState 因为加载是false。然后根据当前状态显示微调器。

    例如,

    class SomeComponent extends React.Component {
      constructor(props) {
        super(props);
        this.state = { loading: false };
      }
    
      loadData(e) {
        e.preventDefault();
        this.setState({ loading: true });
        fetch(....).then( res => res.json()).then( resJson => {
          // do other stuff here.
          this.setState({ loading: false });    
        });
      }
    
      render() {
        return (
          <div>
             <DisplayComponent />
             <button onClick={this.loadData}>Click Me</button>
             { this.state.loading ? <Spinner /> : null }
          </div>
        );
      }
    }
    

    您可以查看react conditional rendering

    【讨论】:

      猜你喜欢
      • 2019-10-29
      • 2011-06-21
      • 2021-09-29
      • 1970-01-01
      • 1970-01-01
      • 2014-08-23
      • 1970-01-01
      • 2018-05-02
      相关资源
      最近更新 更多