Reputation: 1417
In my project I am migrating to React and so not loading JQuery. Since I don't have JQuery anymore, for AJAX calls I am using fetch. With JQuery I can hook the start and end of AJAX calls so it's very easy to change the cursor to a spinner. I can't find similar hooks in fetch. Is there a way to do this other than changing it in each individual AJAX call?
Lots of Googling just kept finding answers about... JQuery.
Upvotes: 9
Views: 11808
Reputation: 1728
The main issue I have found using the setting manually a "loading" flag to true / false is that is quite easy to skip by mistake the set to false flag and your application would like hanged), and on the other hand I dont' want to mess up when several ajax request are being called in parallel, this comes even more harder when you want to track some of them, but in other cases you want to make silent requests.
I have created a microlibrary called react-promise-tracker to handle this in a more automatic way. How it works
Install promise tracker
npm install react-promise-tracker --save
Implement a simple loading indicator component (you can add the style you need for it), and wrap it with the 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);
Instantiate it in your application root component:
render(
<div>
<App />
<LoadingIndicator/>
</div>,
document.getElementById("root")
);
Whenever you want to track a fetch or any promise based call (including async/await), just wrap it with the react-promise-tracker function trackPromise:
import { trackPromise } from 'react-promise-tracker';
export class MyComponent extends Component {
componentWillMount() {
trackPromise(
userAPI.fetchUsers()
.then((users) => {
this.setState({
users,
})
})
);
}
// ...
}
More resources: - Step by step tutorial (including adding a nice looking spinner): https://www.basefactor.com/react-how-to-display-a-loading-indicator-on-fetch-calls
Live sample: https://stackblitz.com/edit/react-promise-tracker-default-area-sample
Github page: https://github.com/Lemoncode/react-promise-tracker
Upvotes: 2
Reputation: 11629
There you go, I think the code is pretty much self-explanatory:
// 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");
});
Here's a live example: https://jsfiddle.net/4fxfcp7g/4/
Upvotes: 10
Reputation: 296
Fetch in thenable, you can add spinner stop function in then(), that is called after response is received. And wrap it in another function for headers and 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
});
Upvotes: 2
Reputation: 10472
What you can do when the fetch
initially happens, you setState
to loading as true
. Then, once fetch is done, you can setState
as loading is false
. And you display the spinner based on the current state.
For example,
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>
);
}
}
You can check out react conditional rendering
Upvotes: 0