Надеюсь, у вас все хорошо. У меня проблема с использованием 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
, поскольку он еще не был создан.
Решение:
-
Избегайте циклических зависимостей: Вам нужно реорганизовать код, чтобы избежать импорта
store
внутриhttp
файла. Вы можете сделать это, передаваяaccessToken
иrefreshToken
в функциюmakeApiCall
без прямого импортаstore
. - Обновите функцию
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);
}
);
Заключение:
Следуя этим шагам, вы уменьшите вероятность возникновения циклических зависимостей и создадите более чистую архитектуру вашего проекта. Если у вас есть дополнительные вопросы или трудности, не стесняйтесь спрашивать!