Add a loading indicator for a React-Redux app with Promise middleware
I am a newbie to react to contraction. I want to add the download text when the user clicked the search button and fired the text when returning data or completing an action. In a normal asynchronous function, I can simply toggle the isLoading flag before and after the call.
However, in my application, I am dispatching an action that returns "type" and "payload" which is a promise. A redux-prom middleware which then automatically transforms this payload and sends it to the reducer. In other words, the middleware executes the .en action for the promise behind the scenes and gives my reducer the correct data.
In this case, I'm not sure how to add the download text to my view, because as soon as I call this.props.getIdByName (this.state.value), I don't know when the data will be returned. The logical place for me would be inside the reducer, since this is when the data will come back. However, I believe it would be a bad thing because reducers shouldn't be doing this kind of task?
Inside my component, I have the following function to send
handleSubmit(e) {
e.preventDefault();
this.props.getIdByName(this.state.value);
}
Inside the actions / index.js file I have the following action generator
export function getIdByName(name) {
const FIELD = '/characters'
let encodedName = encodeURIComponent(name.trim());
let searchUrl = ROOT_URL + FIELD + '?ts=' + TS + '&apikey=' + PUBLIC_KEY + '&hash=' + HASH + '&name=' + encodedName;
const request = axios.get(searchUrl)
return {
type: GET_ID_BY_NAME,
payload: request
}
}
Inside my gearboxes / gearboxes .jsx
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case GET_ID_BY_NAME:
console.log(action.payload.data.data.results[0].id); <-- here the data comes back correctly because reduer called the promise and gave back the data for me
return {...state, id: action.payload.data.data.results[0].id};
default:
return state;
}
}
And in my main index.js file I have a repository created with the following middleware
const createStoreWithMiddleware = applyMiddleware(
promise,
thunk
)(createStore);
source to share
When you dispatch an activity with Promise
as your payload when used redux-promise
, the middleware redux-promise
will catch the activity, wait for it to Promise
be canceled or rejected, and then retransmit the activity, now with the result Promise
as the payload. This means that you are still dealing with your actions, and also that once you are dealing with it, you know it. So you are correct that a reducer is the right place to do this.
However, you are also correct that reducers should not have side effects. They should only build a new state. Then the answer is to show and hide the loading indicator, changing your application state in Redux :)
We can add a flag with a name isLoading
to our Redux store:
const reducers = {
isLoading(state = false, action) {
switch (action.type) {
case 'START_LOADING':
return true;
case 'GET_ID_BY_NAME':
return false;
default:
return state;
}
},
// ... add the reducers for the rest of your state here
}
export default combineReducers(reducers);
Whenever you are about to call getIdByName
, dispatch an action with a type before calling 'START_LOADING'
. When getIdByName
re-sent with help redux-promise
, you will know that the download is complete and the flag isLoading
will be set to false.
We now have a flag indicating whether we are uploading data to our Redux store. Using this flag, you can conditionally display a loading progress bar. :)
source to share
I'm sure Pedro's answer should start, but I recently did a very accurate one loader/pre-loader
in my application.
The easiest way to do this.
- Create a new key in an object
state
in one of your reducers and name itshowLoader: false
. - In the same container as before (the one with the button), create
mapStateToProps
and get this state propertyshowLoader
. - Inside
container
that contains the button you are trying to call withloader
, add an eventonClick
that triggersaction
, saydisplayLoader
. In the same function, also set the parameterthis.props.showLoader
totrue
-
Create an action
displayLoader
to write something like this:function displayLoader() { return { type: DISPLAY_LOADER, payload: {} } }
-
In gearboxes, catch this action and set
showLoader
back tofalse
when your desired one is receivedpayload
.
Hope it helps!
source to share
I think we won't be able to rationally handle the load status with redux-prom, because redux-promise doesn't provide a mechanism to handle the middle situation from the very beginning of the promise to the end of the promise. I think we should abandon the use of redux promise for conveniently showing / hiding UI loading. We may need to use middleware like redux-loading-promise-middleware .
source to share