使用 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
    });
// ...