Я делаю веб-приложение React и новичок. Когда я использовал Context API, у меня возникла проблема.
Вот мой код.
import React, { createContext, useState, useEffect } from "react";
export const RoleContext = createContext({
optionRole: "",
setOptionRole: () => {},
accessToken: "",
setAccessToken: () => {},
passwordChecked: false,
setPasswordChecked: () => {},
});
export const RoleProvider = ({ children }) => {
const [optionRole, setOptionRole] = useState(() => {
return localStorage.getItem("optionRole") || "";
});
const [accessToken, setAccessToken] = useState("");
const [passwordChecked, setPasswordChecked] = useState(false);
useEffect(() => {
const refreshAccessToken = async () => {
try {
const response = await fetch("/auth/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
});
if (!response.ok) {
throw new Error("Failed to refresh access token");
}
const data = await response.json();
setAccessToken(data.accessToken);
} catch (error) {
console.error("Failed to refresh access token:", error);
// logout();
}
};
if (!accessToken) {
refreshAccessToken();
}
}, [accessToken]);
useEffect(() => {
localStorage.setItem("optionRole", optionRole);
}, [optionRole]);
const roleCtx = {
optionRole,
setOptionRole,
accessToken,
setAccessToken,
passwordChecked,
setPasswordChecked,
};
return (
<RoleContext.Provider value = {roleCtx}>{children}</RoleContext.Provider>
);
};
Имя контекста — RoleContext. Это означает, что компонент будет иметь дело с ролью. Но мне нужен другой контекстный API. Если я создам еще один Context API. Код будет беспорядочным. Например, я хочу сделать MathContext. Я могу использовать это таким образом. И предположим, что мне больше хочется Context API, чем больше я хочу сделать, тем сложнее код.
<SubjectProvider>
<MathProvider>
<RoleProvider>
<App/>
</RoleProvider>
<MathProvider>
</SubjectProvider>
Есть ли хороший способ использовать Context API?
извините, я имею в виду, что проблема не в ошибке. Это означает, что код может быть беспорядочным, если я создам много контекстов. Это проблема



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Если вас беспокоит проблема, заключающаяся в том, что у вас много поставщиков контекста, вы можете избежать ее, переместив свою логику в редюсеры (useReducer) и используя единственный Context Provider, чтобы поделиться состоянием вашего редюсера и отправить его действие вниз в дерево компонентов.
Хорошей практикой в отношении вашего вопроса является создание нескольких редукторов для каждой вашей логической единицы/цели, и благодаря этому подходу вы будете следовать шаблону единой ответственности, и эти редукторы будет легче поддерживать.
Затем вы можете объединить свои редукторы в один для централизованного источника истины и использовать это rootReducer в контексте, чтобы сделать состояние и отправку доступными для детей.
В этом случае у вас будет только один поставщик контекста, но он может содержать несколько редукторов, и каждый редуктор будет отвечать за свою собственную цель, поэтому вы получите свою функциональность без множества поставщиков контекста.
Вот пример кода:
счетчик.редуктор
export const initialCounterState = { count: 0 };
export function counterReducer(state = initialCounterState, action) {
switch (action.type) {
case "INCREMENT":
return { count: state.count + 1 };
case "DECREMENT":
return { count: state.count - 1 };
default:
return state;
}
}
тема.редуктор
export const initialThemeState = { theme: "light" };
export function themeReducer(state = initialThemeState, action) {
switch (action.type) {
case "TOGGLE_THEME":
return { theme: state.theme === "light" ? "dark" : "light" };
default:
return state;
}
}
root.reducer, где несколько редукторов объединены вместе
import { counterReducer, initialCounterState } from "./counter.reducer";
import { themeReducer, initialThemeState } from "./theme.reducer";
export function rootReducer(state, action) {
return {
counter: counterReducer(state.counter, action),
theme: themeReducer(state.theme, action)
};
}
export const initialRootState = {
counter: initialCounterState,
theme: initialThemeState
};
AppContext, который будет использовать ваш корневой редуктор, чтобы сделать его доступным для детей:
import React, { createContext, useReducer, useContext } from "react";
import { rootReducer, initialRootState } from "./reducers/index";
const AppCtx = createContext();
export function AppProvider({ children }) {
const [state, dispatch] = useReducer(rootReducer, initialRootState);
return (
<AppCtx.Provider value = {{ state, dispatch }}>{children}</AppCtx.Provider>
);
}
export function useAppContext() {
const ctx = useContext(AppCtx);
if (!ctx) {
throw new Error("useAppContext must be used within a AppProvider");
}
return ctx;
}
import React from "react";
import { useAppContext } from "./AppContext";
const Counter = () => {
const { state, dispatch } = useAppContext();
const { count } = state.counter;
return (
<div>
<h1>Count: {count}</h1>
<button onClick = {() => dispatch({ type: "INCREMENT" })}>Increment</button>
<button onClick = {() => dispatch({ type: "DECREMENT" })}>Decrement</button>
</div>
);
};
export default Counter;
Переключатель тем
import React from "react";
import { useAppContext } from "./AppContext";
const ThemeSwitcher = () => {
const { state, dispatch } = useAppContext();
const { theme } = state.theme;
return (
<div>
<h1>Current Theme: {theme}</h1>
<button onClick = {() => dispatch({ type: "TOGGLE_THEME" })}>
Toggle Theme
</button>
</div>
);
};
export default ThemeSwitcher;
И не забудьте обернуть дочерние компоненты в поставщика контекста:
import React from "react";
import { AppProvider } from "./AppContext";
import Counter from "./Counter";
import ThemeSwitcher from "./ThemeSwitcher";
const App = () => {
return (
<AppProvider>
<Counter />
<ThemeSwitcher />
</AppProvider>
);
};
export default App;
Вот живой пример, который я сделал для этой цели.
Это выглядит хорошо для чистого управления состоянием, но как это работает в контекстах, требующих перехватчиков, таких как перехватчики нескольких эффектов OP? Вы бы серьезно замутили воду, если бы попытались справиться со всеми ними внутри AppProvider
Можно создать специальный хук для любого редюсера, например useCounterReducer, который будет использовать редюсер внутри и может иметь сложную логику и доступ ко всем другим контекстам/эффектам и т. д., как и любой другой хук. Затем вы можете использовать этот хук внутри AppProvider, чтобы получить окончательное состояние редуктора. Кстати. ОП спросил, как быть с несколькими провайдерами контекста, потому что его беспокоит количество провайдеров (что я могу понять на основе своего собственного опыта), поэтому я предложил ему решение, как справиться с этой проблемой.
Было бы хорошо, если бы вы включили это в свой ответ, поскольку я определенно предвижу, что первый вопрос от ОП будет «как мне использовать свои эффекты» 😅
То, как вы создаете свой контекстный стек, на самом деле является лишь вопросом вашего мнения. Хотя нет проблем с тем, как вы это делаете. Получаете ли вы какие-либо ошибки? Вы упомянули «У меня возникла проблема», но не предоставили никаких подробностей.