Как я могу эффективно использовать React Context API

Я делаю веб-приложение 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?

То, как вы создаете свой контекстный стек, на самом деле является лишь вопросом вашего мнения. Хотя нет проблем с тем, как вы это делаете. Получаете ли вы какие-либо ошибки? Вы упомянули «У меня возникла проблема», но не предоставили никаких подробностей.

Phil 26.07.2024 03:52

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

Aiden 27.07.2024 10:05
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
0
2
54
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Если вас беспокоит проблема, заключающаяся в том, что у вас много поставщиков контекста, вы можете избежать ее, переместив свою логику в редюсеры (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;
}

Components can use it in this way thanks to the hook in the AppContext:
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

Phil 26.07.2024 04:32

Можно создать специальный хук для любого редюсера, например useCounterReducer, который будет использовать редюсер внутри и может иметь сложную логику и доступ ко всем другим контекстам/эффектам и т. д., как и любой другой хук. Затем вы можете использовать этот хук внутри AppProvider, чтобы получить окончательное состояние редуктора. Кстати. ОП спросил, как быть с несколькими провайдерами контекста, потому что его беспокоит количество провайдеров (что я могу понять на основе своего собственного опыта), поэтому я предложил ему решение, как справиться с этой проблемой.

Velidan 26.07.2024 04:55

Было бы хорошо, если бы вы включили это в свой ответ, поскольку я определенно предвижу, что первый вопрос от ОП будет «как мне использовать свои эффекты» 😅

Phil 26.07.2024 05:53

Другие вопросы по теме