使用 fetch API 时在 AJAX 调用期间显示微调器

Posted

技术标签:

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

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

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

【问题讨论】:

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

实时样本:https://stackblitz.com/edit/react-promise-tracker-default-area-sample

Github 页面:https://github.com/Lemoncode/react-promise-tracker

【讨论】:

【参考方案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

【讨论】:

以上是关于使用 fetch API 时在 AJAX 调用期间显示微调器的主要内容,如果未能解决你的问题,请参考以下文章

.NET Core 3.1 中使用 Fetch API 的 Ajax 调用

试图将 jquery AJAX 转换为 Fetch API,如何设置RequestHeader?

基于Promise对象的新一代Ajax API--fetch

如何在 JavaScript 中将 Ajax 转换为 Fetch API?

Ajax新玩法fetch API

fetch api和ajax本质的区别