* chore: rename APIs and Microservices to include "Backend" (#42515) * fix typo * fix typo * undo change * Corrected grammar mistake Corrected a grammar mistake by removing a comma. * change APIs and Microservices cert title * update title * Change APIs and Microservices certi title * Update translations.json * update title * feat(curriculum): rename apis and microservices cert * rename folder structure * rename certificate * rename learn Markdown * apis-and-microservices -> back-end-development-and-apis * update backend meta * update i18n langs and cypress test Co-authored-by: Shaun Hamilton <shauhami020@gmail.com> * fix: add development to front-end libraries (#42512) * fix: added-the-word-Development-to-front-end-libraries * fix/added-the-word-Development-to-front-end-libraries * fix/added-word-development-to-front-end-libraries-in-other-related-files * fix/added-the-word-Development-to-front-end-and-all-related-files * fix/removed-typos-from-last-commit-in-index.md * fix/reverted-changes-that-i-made-to-dependecies * fix/removed xvfg * fix/reverted changes that i made to package.json * remove unwanted changes * front-end-development-libraries changes * rename backend certSlug and README * update i18n folder names and keys * test: add legacy path redirect tests This uses serve.json from the client-config repo, since we currently use that in production * fix: create public dir before moving serve.json * fix: add missing script * refactor: collect redirect tests * test: convert to cy.location for stricter tests * rename certificate folder to 00-certificates * change crowdin config to recognise new certificates location * allow translations to be used Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com> * add forwards slashes to path redirects * fix cypress path tests again * plese cypress * fix: test different challenge Okay so I literally have no idea why this one particular challenge fails in Cypress Firefox ONLY. Tom and I paired and spun a full build instance and confirmed in Firefox the page loads and redirects as expected. Changing to another bootstrap challenge passes Cypress firefox locally. Absolutely boggled by this. AAAAAAAAAAAAAAA * fix: separate the test Okay apparently the test does not work unless we separate it into a different `it` statement. >:( >:( >:( >:( Co-authored-by: Sujal Gupta <55016909+heysujal@users.noreply.github.com> Co-authored-by: Noor Fakhry <65724923+NoorFakhry@users.noreply.github.com> Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Nicholas Carrigan (he/him) <nhcarrigan@gmail.com>
171 lines
5.2 KiB
Markdown
171 lines
5.2 KiB
Markdown
---
|
|
id: 5a24c314108439a4d4036156
|
|
title: Use Middleware to Handle Asynchronous Actions
|
|
challengeType: 6
|
|
forumTopicId: 301451
|
|
dashedName: use-middleware-to-handle-asynchronous-actions
|
|
---
|
|
|
|
# --description--
|
|
|
|
So far these challenges have avoided discussing asynchronous actions, but they are an unavoidable part of web development. At some point you'll need to call asynchronous endpoints in your Redux app, so how do you handle these types of requests? Redux provides middleware designed specifically for this purpose, called Redux Thunk middleware. Here's a brief description how to use this with Redux.
|
|
|
|
To include Redux Thunk middleware, you pass it as an argument to `Redux.applyMiddleware()`. This statement is then provided as a second optional parameter to the `createStore()` function. Take a look at the code at the bottom of the editor to see this. Then, to create an asynchronous action, you return a function in the action creator that takes `dispatch` as an argument. Within this function, you can dispatch actions and perform asynchronous requests.
|
|
|
|
In this example, an asynchronous request is simulated with a `setTimeout()` call. It's common to dispatch an action before initiating any asynchronous behavior so that your application state knows that some data is being requested (this state could display a loading icon, for instance). Then, once you receive the data, you dispatch another action which carries the data as a payload along with information that the action is completed.
|
|
|
|
Remember that you're passing `dispatch` as a parameter to this special action creator. This is what you'll use to dispatch your actions, you simply pass the action directly to dispatch and the middleware takes care of the rest.
|
|
|
|
# --instructions--
|
|
|
|
Write both dispatches in the `handleAsync()` action creator. Dispatch `requestingData()` before the `setTimeout()` (the simulated API call). Then, after you receive the (pretend) data, dispatch the `receivedData()` action, passing in this data. Now you know how to handle asynchronous actions in Redux. Everything else continues to behave as before.
|
|
|
|
# --hints--
|
|
|
|
The `requestingData` action creator should return an object of type equal to the value of `REQUESTING_DATA`.
|
|
|
|
```js
|
|
assert(requestingData().type === REQUESTING_DATA);
|
|
```
|
|
|
|
The `receivedData` action creator should return an object of type equal to the value of `RECEIVED_DATA`.
|
|
|
|
```js
|
|
assert(receivedData('data').type === RECEIVED_DATA);
|
|
```
|
|
|
|
`asyncDataReducer` should be a function.
|
|
|
|
```js
|
|
assert(typeof asyncDataReducer === 'function');
|
|
```
|
|
|
|
Dispatching the `requestingData` action creator should update the store `state` property of fetching to `true`.
|
|
|
|
```js
|
|
assert(
|
|
(function () {
|
|
const initialState = store.getState();
|
|
store.dispatch(requestingData());
|
|
const reqState = store.getState();
|
|
return initialState.fetching === false && reqState.fetching === true;
|
|
})()
|
|
);
|
|
```
|
|
|
|
Dispatching `handleAsync` should dispatch the data request action and then dispatch the received data action after a delay.
|
|
|
|
```js
|
|
assert(
|
|
(function () {
|
|
const noWhiteSpace = __helpers.removeWhiteSpace(handleAsync.toString());
|
|
return (
|
|
noWhiteSpace.includes('dispatch(requestingData())') === true &&
|
|
noWhiteSpace.includes('dispatch(receivedData(data))') === true
|
|
);
|
|
})()
|
|
);
|
|
```
|
|
|
|
# --seed--
|
|
|
|
## --seed-contents--
|
|
|
|
```js
|
|
const REQUESTING_DATA = 'REQUESTING_DATA'
|
|
const RECEIVED_DATA = 'RECEIVED_DATA'
|
|
|
|
const requestingData = () => { return {type: REQUESTING_DATA} }
|
|
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
|
|
|
|
const handleAsync = () => {
|
|
return function(dispatch) {
|
|
// Dispatch request action here
|
|
|
|
setTimeout(function() {
|
|
let data = {
|
|
users: ['Jeff', 'William', 'Alice']
|
|
}
|
|
// Dispatch received data action here
|
|
|
|
}, 2500);
|
|
}
|
|
};
|
|
|
|
const defaultState = {
|
|
fetching: false,
|
|
users: []
|
|
};
|
|
|
|
const asyncDataReducer = (state = defaultState, action) => {
|
|
switch(action.type) {
|
|
case REQUESTING_DATA:
|
|
return {
|
|
fetching: true,
|
|
users: []
|
|
}
|
|
case RECEIVED_DATA:
|
|
return {
|
|
fetching: false,
|
|
users: action.users
|
|
}
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
|
|
const store = Redux.createStore(
|
|
asyncDataReducer,
|
|
Redux.applyMiddleware(ReduxThunk.default)
|
|
);
|
|
```
|
|
|
|
# --solutions--
|
|
|
|
```js
|
|
const REQUESTING_DATA = 'REQUESTING_DATA'
|
|
const RECEIVED_DATA = 'RECEIVED_DATA'
|
|
|
|
const requestingData = () => { return {type: REQUESTING_DATA} }
|
|
const receivedData = (data) => { return {type: RECEIVED_DATA, users: data.users} }
|
|
|
|
const handleAsync = () => {
|
|
return function(dispatch) {
|
|
dispatch(requestingData());
|
|
setTimeout(function() {
|
|
let data = {
|
|
users: ['Jeff', 'William', 'Alice']
|
|
}
|
|
dispatch(receivedData(data));
|
|
}, 2500);
|
|
}
|
|
};
|
|
|
|
const defaultState = {
|
|
fetching: false,
|
|
users: []
|
|
};
|
|
|
|
const asyncDataReducer = (state = defaultState, action) => {
|
|
switch(action.type) {
|
|
case REQUESTING_DATA:
|
|
return {
|
|
fetching: true,
|
|
users: []
|
|
}
|
|
case RECEIVED_DATA:
|
|
return {
|
|
fetching: false,
|
|
users: action.users
|
|
}
|
|
default:
|
|
return state;
|
|
}
|
|
};
|
|
|
|
const store = Redux.createStore(
|
|
asyncDataReducer,
|
|
Redux.applyMiddleware(ReduxThunk.default)
|
|
);
|
|
```
|