saga не прослушивает действие, отправленное из пользовательского промежуточного программного обеспечения

#javascript #reactjs #redux #redux-saga #redux-toolkit

#javascript #reactjs #redux #redux-saga #redux-toolkit

Вопрос:

Вариант использования — мне нужно проверить, истек ли срок действия токена или нет. Если срок действия токена истек, мне нужно вызвать api, чтобы получить новый токен доступа. Токен доступа истекает каждые 30 минут. Для этого я пытался сделать следующее.

  1. Напишите пользовательское промежуточное программное обеспечение, вызываемое checkTokenExpirationMiddleware store.js
  2. checkToken Действие отправки, чтобы saga могла перехватить это действие и вызвать вызов api для получения нового токена доступа.
  3. Внедрить редуктор и saga на app.js

Проблема в том, что saga не смогла прослушать это действие.

Вот код того, что я сделал

store/index.js

 import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit";
import { createInjectorsEnhancer, forceReducerReload } from "redux-injectors";
import createSagaMiddleware from "redux-saga";

const checkTokenExpirationMiddleware = (store) => (next) => (action) => {
  console.log("#####action#####", action);
  const token = JSON.parse(localStorage.getItem("token"));
  if (token amp;amp; jwt_decode(token).exp < Date.now() / 1000) {
    // dispatch checkToken action so that saga will listen this action
    // and call api for another access token
    const result = next(actions.checkToken());
    return resu<
    // store.dispatch(actions.checkToken());
  }
  next(action);
};

export function configureAppStore() {
  const reduxSagaMonitorOptions = {};
  const sagaMiddleware = createSagaMiddleware(reduxSagaMonitorOptions);
  const { run: runSaga } = sagaMiddleware;

  // // Create the store with saga middleware
  const middlewares = [sagaMiddleware, checkTokenExpirationMiddleware];

  const enhancers = [
    createInjectorsEnhancer({
      createReducer,
      runSaga,
    }),
  ];

  const store = configureStore({
    reducer: createReducer(),
    middleware: [...getDefaultMiddleware(), ...middlewares],
    devTools:
      /* istanbul ignore next line */
      process.env.NODE_ENV !== "production" ||
      process.env.PUBLIC_URL.length > 0,
    enhancers,
  });

  // Make reducers hot reloadable, see http://mxs.is/googmo
  /* istanbul ignore next */
  if (module.hot) {
    module.hot.accept("./reducers", () => {
      forceReducerReload(store);
    });
  }

  return store;
}
  

store/globalSaga.js

 export function* checkToken() {
  console.log("checkToken sagaå");
  debugger;
  // need to call api to get new access token
}

export function* globalSaga() {
  //  watches for
  console.log("global saga watcher");
  yield all([
    takeLatest(actions.logout, logout),
    takeEvery(actions.checkToken, checkToken),
  ]);
}
  

App.js

 function App() {
  // const dispatch = useDispatch();
  console.log("injecting saga and reducer");
  useInjectReducer({ key: sliceKey, reducer });
  useInjectSaga({ key: sliceKey, saga: globalSaga });
  // React.useEffect(() => {
    // this executes saga
  //   console.log("dispatching");
  //   dispatch(actions.checkToken());
  // }, []);
  return (
    <React.Suspense fallback={<LoadingSpinner />}>
      <Router>
        <RouterApp />
      </Router>
    </React.Suspense>
  );
}
  

почему после отправки checkToken действия из промежуточного программного обеспечения saga не смогла его перехватить?

Примечание: я использую reduxjs / toolkit.

Обновить

Я извлек api отсюда, чтобы получить новый токен доступа. Теперь я могу получить токен доступа, и бесконечного цикла нет. Однако есть проблема, он не выполняет api, который ранее был сбойным из-за недействительного токена (истек). Как я могу теперь снова выполнить этот api после получения нового токена доступа?

 const checkTokenExpirationMiddleware = ({ dispatch, getState }) => (
  next
) => async (action) => {
  const isAlreadyFetchingAccessToken = getState()?.global
    .isAlreadyFetchingAccessToken;
  const user = JSON.parse(localStorage.getItem("userInfo"));
  const access_token = JSON.parse(localStorage.getItem("access_token"));
  if (
    !isAlreadyFetchingAccessToken amp;amp;
    access_token amp;amp;
    jwt_decode(access_token).exp < Date.now() / 1000
  ) {
    const payload = {
      data: {
        refresh_token: access_token,
      },
    };
    const response = await request(
      "https://localhost:3000/token/refresh",
      payload,
      "POST"
    );
    console.log("#########response#########", response);
    if (response?.status === 200) {
      localStorage.setItem("token", JSON.stringify(response?.access_token));
      dispatch(actions.checkTokenSuccess());
      next(action); // now need to call the api with new access token which could not execute due to invalid token(expired)
      console.log("action dispatching", action);
    }
  }
  next(action);
};
  

Ответ №1:

Это потому, что промежуточное программное обеспечение формирует конвейер, а storeAPI.dispatch next функции и выполняют разные функции

  • storeAPI.dispatch(action) отправляет действие в начало цепочки промежуточного программного обеспечения, поэтому оно пройдет через все промежуточное программное обеспечение (включая это) next(action) , отправляет действие следующему промежуточному программному обеспечению в цепочке.

Итак, если вы настроите вещи как applyMiddleware(sagaMiddleware, myCustomMiddleware) , и ваши пользовательские вызовы промежуточного next(action) программного обеспечения, действие будет отправлено прямо в редукторы, и промежуточное программное обеспечение saga никогда не увидит его.

Если вы вызовете storeAPI.dispatch(action) вместо этого, он перейдет к началу конвейера промежуточного программного обеспечения, который sagaMiddleware .

Комментарии:

1. Спасибо за прекрасное объяснение. Повезло получить объяснение от сопровождающего redux :). Я пытался store.dispatch(actions.checkToken()) , но это создает бесконечный цикл, и saga не улавливает это действие. Есть ли какие-либо проблемы в моей настройке?

2. ДА. Ваше пользовательское промежуточное программное обеспечение всегда пытается прочитать токен из localStorage и всегда отправляет действие. Вам нужно как-то изменить эту логику, чтобы она не отправлялась каждый раз.

3. Извините, я пока не смог исправить. Можете ли вы помочь мне, где я должен точно изменить логику?

4. Я обновил свой вопрос новым кодом, который я пробовал. Таким образом, нет проблемы с бесконечным циклом. Но api, который был сброшен из-за недопустимого токена, не вызывается после получения нового токена доступа.