i have button in my index.jsx page which dispatch an activate action
<StickyButton type="button" onClick={() => dispatch(activate(values))}>
<span>{t("share.button.continue")}</span>
</StickyButton>;
and this action, dispatch another action call isRequesting() which i use it in my spinner to show spinner, then call a method in authService to activate user :
export const activate = (model) => async (dispatch) => {
await dispatch(isRequesting());
authService
.activate(model)
.then(async (result) => {
authService.setSignedUp();
await dispatch(fetchProfile());
await dispatch(isRequested());
history.push(`${routes.sign_up.base}${routes.sign_up.activated}`);
})
.catch((error) => {
dispatch(errorOccurred());
});
};
and the authService activate function is :
function activate(model) {
let request = {
lang: encryptService.aesStaticEncrypt(localeService.getActiveLanguage()),
ver: encryptService.aesStaticEncrypt(config.app_version),
phoneNumber: encryptService.aesStaticEncrypt(utilService.formatMobileWithPrefix(userService.getMobile())),
invoice: encryptService.aesStaticEncrypt(utilService.getRandomDigit()),
value: {
activationCode: encryptService.aesStaticEncrypt(utilService.formatActivationCode(model.activation_code)),
},
};
return api
.post(`${config.apiUrl}/GATEWAY/ACTIVATIONGATEWAY/V1/Activate`, request)
.then(async (result) => {
return Promise.resolve(result);
})
.catch((error) => {
utilService.handleError(error);
return Promise.reject(error);
});
}
and spinner component :
export const FullPageSpinner = () => {
const { isRequesting } = useSelector((state) => state.request);
console.log("FullPageSpinner");
console.log(isRequesting);
return (
<div
css={{
position: "fixed",
width: "100%",
height: "100%",
display: "flex",
justifyContent: "center",
fontSize: "3rem",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "#00000038",
opacity: isRequesting ? 1 : 0,
zIndex: isRequesting ? "9999999" : "-1",
}}
>
<div css={{ alignSelf: "center", color: "#3e3e3e" }}>
<Spinner />
</div>
</div>
);
};
request reducer code :
import * as types from "../actionTypes/request";
const initialState = {
isRequesting: false,
isRequested: false,
};
export default function requestReducer(state = initialState, action) {
if (action.type === types.IsRequesting) {
return {
...state,
isRequesting: true,
isRequested: false
};
}
if (action.type === types.IsRequested) {
return {
...state,
isRequesting: false,
isRequested: true
};
}
if (action.type === types.ErrorOccurred) {
return {
...state,
isRequesting: false,
isRequested: true
};
}
return state;
}
root reducer :
import { combineReducers } from "redux";
import profileReducer from "./profile";
import requestReducer from "./request";
import appReducer from "./app";
const rootReducer = combineReducers({
profile: profileReducer,
request: requestReducer,
app: appReducer,
});
export default rootReducer;
and create store :
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__()) || compose
)
);
request actions :
import * as request_types from "../actionTypes/request";
export const isRequesting = () => {
console.log("isRequesting");
return {
type: request_types.IsRequesting,
};
};
export const isRequested = (messages) => {
console.log("isRequested");
return {
type: request_types.IsRequested,
payload: messages,
};
};
request reducer :
const initialState = {
isRequesting: false,
isRequested: false,
};
export default function requestReducer(state = initialState, action) {
if (action.type === types.IsRequesting) {
return {
...state,
isRequesting: true,
isRequested: false
};
}
if (action.type === types.IsRequested) {
toastService.notify(action.payload);
return {
...state,
isRequesting: false,
isRequested: true
};
}
if (action.type === types.ErrorOccurred) {
return {
...state,
isRequesting: false,
isRequested: true
};
}
return state;
}
and AppComponent where i put the FullPageSpinner to render based on isRequesting
const App = () => {
const { ltr } = useSelector((state) => state.language);
return (
<React.Fragment>
<Routing />
<ToastContainer
position="bottom-center"
autoClose={config.toast_auto_close}
transition={Flip}
{...(!ltr && { rtl: true })}
/>
<FullPageSpinner />
</React.Fragment>
);
};
export default App;
the problem is when i dispatch isRequesting(), the state is changing but the spinner does not show up and it wait until the response of the authService.activate function which it could take some time to return. i want the spinner to show immediately after i dispatch isRequesting() and not wait for the response of
Try to remove unnecessary async/await from activate action creator:
export const activate = (model) => (dispatch) => { // async here is not necessary
dispatch(isRequesting()); // await here is not necessary
authService
.activate(model)
.then(async (result) => {
authService.setSignedUp();
await dispatch(fetchProfile());
dispatch(isRequested()); // await here is not necessary
history.push(`${routes.sign_up.base}${routes.sign_up.activated}`);
})
.catch((error) => {
dispatch(errorOccurred());
});
};
EDIT:
I looked at example you added in a sandbox and your implementation of authService.activate()
was not asynchronous there.
I provided fix here: https://stackblitz.com/edit/react-xplsqp?file=services/auth.js
Your original authService.activate()
is probably blocking and not asynchronous also.
So check your api.post
if it is asynchronous. I also suggest some improvements of your code (check my comments):
//autService.activate
//...
return api
.post(`${config.apiUrl}/GATEWAY/ACTIVATIONGATEWAY/V1/Activate`, request)
.then(async (result) => { // remove async from here
return Promise.resolve(result); // do not use Promise.resolve, just "return result;" is correct
})
.catch((error) => {
utilService.handleError(error);
return Promise.reject(error); // do not use Promise.reject here, if you want to recatch this error in next chain just "throw error;" instead
});
// ...