Перепишите это название: “React с использованием Redux”

Вопросы и ответы

Надеюсь, у вас все хорошо. У меня проблема с использованием React вместе с Redux, проблема в следующем:
Проблема

Я получаю ошибку “ReferenceError: Cannot access ‘authSlice’ before initialization”. Думаю, проблема в том, что у меня есть класс под названием http, и в нем я импортирую store, а затем в моем классе slice я импортирую метод http, я думаю, именно это и является причиной. Но это потому, что мне нужно получить доступ к store, я прикрепляю свой store, segment и класс http:

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import { HOST } from "../http/utils";
import makeApiCall from "../http"; //Возможная причина

const accessToken = localStorage.getItem("accessToken");
const refreshToken = localStorage.getItem("refreshToken");
const username = localStorage.getItem("username");

const initialState = {
  username: username || null,
  password: null,
  isLoading: false,
  error: null,
  accessToken: accessToken || null,
  refreshToken: refreshToken || null,
};

export const loginUser = createAsyncThunk(
  "api/login",
  async (payload, { rejectWithValue }) => {
    const url = HOST.concat("api/token/");
    return makeApiCall(url, "POST", payload, rejectWithValue);
  },
);

export const authSlice = createSlice({
  name: "auth",
  initialState,
  reducers: {
    logout: (state) => {
      state.accessToken = null;
      state.refreshToken = null;
      state.username = null;
      state.password = null;
      localStorage.removeItem("accessToken");
      localStorage.removeItem("refreshToken");
    },
    refresh: (state, action) => {
      state.refreshToken = action.payload.refresh;
      state.accessToken = action.payload.access;
    },
    setError: (state, action) => {
      state.error = action.payload;
      state.isLoading = false;
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(loginUser.pending, (state, action) => {
        state.isLoading = true;
      })
      .addCase(loginUser.fulfilled, (state, action) => {
        state.accessToken = action.payload.access;
        state.refreshToken = action.payload.refresh;
        localStorage.setItem("accessToken", state.accessToken);
        localStorage.setItem("refreshToken", state.refreshToken);
        state.isLoading = false;
      })
      .addCase(loginUser.rejected, (state, action) => {
        state.error = action.payload;
        state.isLoading = false;
      });
  },
});

export const { logout, setError, refresh } = authSlice.actions;
export default authSlice.reducer;

Вот моя конфигурация store:

import { configureStore } from "@reduxjs/toolkit";
import { authSlice } from "./authSlice";
import { userSlice } from "./userSlice";

export const store = configureStore({
  reducer: {
    auth: authSlice.reducer,
    user: userSlice.reducer,
  },
});

А вот мой класс http:

import axios from "axios";
import { store } from "../redux/store"; //Возможная причина
import { HOST } from "./utils";

const getHeaders = () => {
  const headers = {
    Accept: "application/json",
    "Content-type": "Application/json",
  };
  const access = store.getState().auth.accessToken;
  if (access) {
    headers.Authorization = `Bearer ${access}`;
  }
  return headers;
};

const refreshAccessToken = async function () {
  return axios
    .request({
      method: "POST",
      url: HOST.concat("/api/v1/auth/refresh/"),
      data: {
        refresh: store.getState().auth.refreshToken,
      },
    })
    .then((response) => {
      if (response.status === 200) {
        store.dispatch({
          type: "auth/refresh",
          payload: response.data,
        });
      }
    })
    .catch(() => {
      store.dispatch({
        type: "auth/logout",
      });
    });
};

const axiosClient = axios.create();

axiosClient.interceptors.request.use(
  async (config) => {
    config.headers = getHeaders();
    return config;
  },
  (error) => {
    return Promise.reject(error);
  },
);

axiosClient.interceptors.response.use(
  (response) => {
    return response;
  },
  async function (error) {
    let response_status;
    try {
      response_status = error.response.status;
    } catch (e) {
      response_status = 200;
    }
    const originalRequest = error.config;
    if (response_status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      await refreshAccessToken();
      return axiosClient(originalRequest);
    }

    return Promise.reject(error);
  },
);

export default async function makeApiCall(url, method, data, rejectWithValue) {
  return new Promise((resolve, reject) => {
    axiosClient
      .request({
        url,
        method,
        data,
      })
      .then((response) => {
        resolve(response.data);
      })
      .catch((error) => {
        reject(rejectWithValue(error.response.data));
      });
  });
}

Надеюсь, вы сможете мне помочь. Если вам нужно, чтобы я показал что-то еще, просто спросите. Большое спасибо за то, что прочитали мою проблему, надеюсь, вы сможете помочь мне.

Надеюсь, вы сможете помочь мне решить проблему, я использую React и Redux, но получаю:

ReferenceError: Cannot access ‘authSlice’ before initialization

Ответ

Доброго дня!

Ваше сообщение указывает на распространенную проблему в JavaScript: ReferenceError: Cannot access 'authSlice' before initialization. Эта ошибка возникает, когда вы пытаетесь использовать переменную до ее объявления. В вашем случае это связано с циклическими зависимостями между файлами.

Проблема:

Вы импортируете store в вашем классе http, а затем создаете authSlice, который также зависит от http. Это создает циклическую зависимость, и интерпретатор JavaScript не может инициализировать authSlice, поскольку он еще не был создан.

Решение:

  1. Избегайте циклических зависимостей: Вам нужно реорганизовать код, чтобы избежать импорта store внутри http файла. Вы можете сделать это, передавая accessToken и refreshToken в функцию makeApiCall без прямого импорта store.

  2. Обновите функцию makeApiCall: Передавайте необходимые данные в функцию и не используйте общий store внутри http.

Пример кода, как это можно реализовать:

// http.js
import axios from "axios";
import { HOST } from "./utils";

const getHeaders = (accessToken) => {
  const headers = {
    Accept: "application/json",
    "Content-type": "Application/json",
  };
  if (accessToken) {
    headers.Authorization = `Bearer ${accessToken}`;
  }
  return headers;
};

const refreshAccessToken = async (refreshToken, dispatch) => {
  return axios
    .request({
      method: "POST",
      url: HOST.concat("/api/v1/auth/refresh/"),
      data: {
        refresh: refreshToken,
      },
    })
    .then((response) => {
      if (response.status === 200) {
        dispatch({
          type: "auth/refresh",
          payload: response.data,
        });
      }
    })
    .catch(() => {
      dispatch({
        type: "auth/logout",
      });
    });
};

// Изменяем makeApiCall
export default async function makeApiCall(url, method, data, accessToken, refreshToken, dispatch, rejectWithValue) {
  const headers = getHeaders(accessToken);

  return new Promise((resolve, reject) => {
    axios
      .request({
        url,
        method,
        data,
        headers,
      })
      .then((response) => {
        resolve(response.data);
      })
      .catch(async (error) => {
        if (error.response.status === 401) {
          await refreshAccessToken(refreshToken, dispatch);
          // Рекурсивно повторяем запрос
          return makeApiCall(url, method, data, accessToken, refreshToken, dispatch, rejectWithValue);
        }
        reject(rejectWithValue(error.response.data));
      });
  });
}

После этих изменений:

  • Убедитесь, что при вызове makeApiCall в вашей функции loginUser вы передаете accessToken, refreshToken и dispatch так, чтобы они были доступны для обновления состояния в случае необходимости.

Обновление функции loginUser:

export const loginUser = createAsyncThunk(
  "api/login",
  async (payload, { dispatch, rejectWithValue }) => {
    const url = HOST.concat("api/token/");
    const accessToken = localStorage.getItem("accessToken");
    const refreshToken = localStorage.getItem("refreshToken");
    return makeApiCall(url, "POST", payload, accessToken, refreshToken, dispatch, rejectWithValue);
  }
);

Заключение:

Следуя этим шагам, вы уменьшите вероятность возникновения циклических зависимостей и создадите более чистую архитектуру вашего проекта. Если у вас есть дополнительные вопросы или трудности, не стесняйтесь спрашивать!

Оцените материал
Добавить комментарий

Капча загружается...