使用 redux 和 react 显示全局微调器的问题
Problem showing global spinner using redux and react
我的 index.jsx 页面中有按钮可以发送激活操作
<StickyButton type="button" onClick={() => dispatch(activate(values))}>
<span>{t("share.button.continue")}</span>
</StickyButton>;
和这个动作,分派另一个动作调用 isRequesting(),我在微调器中使用它来显示微调器,然后调用 authService 中的方法来激活用户:
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());
});
};
authService 激活函数是:
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);
});
}
和微调器组件:
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>
);
};
请求减速器代码:
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;
}
根减速器:
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;
并创建商店:
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__()) || compose
)
);
请求操作:
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,
};
};
请求减速器:
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;
}
和 AppComponent,我将 FullPageSpinner 放置在基于 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;
问题是当我调度 isRequesting() 时,状态正在改变,但微调器没有显示,它一直等到 authService.activate 函数的响应,这可能需要一些时间才能 return.
我希望微调器在我发送 isRequesting() 后立即显示,而不是等待
的响应
尝试从激活操作创建者中删除不必要的 async/await:
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());
});
};
编辑:
我查看了您在沙箱中添加的示例,发现您的 authService.activate()
实现不是异步的。
我在这里提供了修复:
https://stackblitz.com/edit/react-xplsqp?file=services/auth.js
您原来的 authService.activate()
可能是阻塞的,也不是异步的。
所以检查你的 api.post
是否是异步的。我还建议对您的代码进行一些改进(查看我的评论):
//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
});
// ...
我的 index.jsx 页面中有按钮可以发送激活操作
<StickyButton type="button" onClick={() => dispatch(activate(values))}>
<span>{t("share.button.continue")}</span>
</StickyButton>;
和这个动作,分派另一个动作调用 isRequesting(),我在微调器中使用它来显示微调器,然后调用 authService 中的方法来激活用户:
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());
});
};
authService 激活函数是:
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);
});
}
和微调器组件:
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>
);
};
请求减速器代码:
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;
}
根减速器:
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;
并创建商店:
const store = createStore(
reducer,
compose(
applyMiddleware(thunk),
(window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__()) || compose
)
);
请求操作:
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,
};
};
请求减速器:
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;
}
和 AppComponent,我将 FullPageSpinner 放置在基于 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;
问题是当我调度 isRequesting() 时,状态正在改变,但微调器没有显示,它一直等到 authService.activate 函数的响应,这可能需要一些时间才能 return. 我希望微调器在我发送 isRequesting() 后立即显示,而不是等待
的响应尝试从激活操作创建者中删除不必要的 async/await:
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());
});
};
编辑:
我查看了您在沙箱中添加的示例,发现您的 authService.activate()
实现不是异步的。
我在这里提供了修复: https://stackblitz.com/edit/react-xplsqp?file=services/auth.js
您原来的 authService.activate()
可能是阻塞的,也不是异步的。
所以检查你的 api.post
是否是异步的。我还建议对您的代码进行一些改进(查看我的评论):
//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
});
// ...