如何使用 React Context API 处理多个 Context?

How to handle multiple Contexts using React Context API?

我对 React 的上下文有疑问 API。我对 React 的编码水平是初学者。

我正在构建一个具有 8 个上下文的应用程序,它们可能会在项目的未来增加。它们是我应用程序不同元素的基本 CRUD 上下文,没有太多复杂性。

在编写我的应用程序时,我注意到在我的 App.js

中创建了一个嵌套的上下文地狱

为了提供更多信息,我将解释该应用程序的一部分。我有针对教练、运动员、法庭等的 CRUD 操作的上下文

在我的 /src 目录下的文件夹结构中,我有一个 /context 目录,里面每个实体都有一个单独的文件夹。让我们以教练为例。在 /src/context/coach 目录中我有 3 个文件。一个 coachContext.js、一个 coachReducer.js 和一个 CoachState.js

coachContext.js 文件的内容:

import { createContext } from "react";

const coachContext = createContext();

export default coachContext;

coachReducer.js 文件的内容:

const coachReducer = (state, action) => {
    switch (action.type) {
        case "GET_COACHES":
            return {
                ...state,
                coaches: action.payload,
            };
        case "SET_CURRENT_COACH":
            return {
                ...state,
                coach: action.payload,
                loading: false,
            };

        default:
            return state;
    }
};

export default coachReducer;

CoachState.js 文件的内容:

import { useReducer } from "react";
import coachContext from "./coachContext";
import coachReducer from "./coachReducer";

const CoachState = (props) => {
    const initialState = {
        coaches: [],
        coach: [],
        loading: false,
    };

    const [state, dispatch] = useReducer(coachReducer, initialState);

    // Function to Add coach
    // Function to Delete coach
    // Function to Set current coach
    // Function to clear current coach
    // Function to Update coach

    return (
        <coachContext.Provider
            value={{
                coaches: state.coaches,
                coach: state.coach,
                loading: state.loading,
            }}
        >
            {props.children}
        </coachContext.Provider>
    );
};

export default CoachState;

运动员背景、法院背景和我申请的所有其他元素也是如此。

最后,在我的 App.js 中,我有:

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./pages/Home";
import Coaches from "./pages/Coaches";
import Athletes from "./pages/Athletes";
import Courts from "./pages/Courts";

import CoachState from "./context/coach/CoachState";
import AthleteState from "./context/athlete/AthleteState";
import CourtState from "./context/court/CourtState";

function App() {
    return (
        <CourtState>
          <AthleteState>
            <CoachState>
              <Router>
                <Switch>
                  <Route exact path="/" component={Home}></Route>
                  <Route exact path="/coaches" component={Coaches}></Route>
                  <Route exact path="/athletes" component={Athletes}></Route>
                  <Route exact path="/courts" component={Courts}></Route>
                </Switch>
              </Router>
            </CoachState>
          </AthleteState>
        </CourtState>
    );
}

export default App;

当我写完我的其他上下文时,如您所知,它们将像所有当前状态一样包装路由器。所以会出现一个很大的嵌套“问题”。

我想就如何解决这个嵌套上下文问题提出任何建议?我做出使用 Context API 而不是 Redux 开发我的应用程序的决定是否正确?

您可以在一个上下文提供程序中添加所有需要的值,然后使用自定义挂钩获取数据或函数,而不是使用多个上下文提供程序然后 useContext 从每个上下文提供程序获取每个值你需要

这会减少使用的上下文提供程序的数量,但不会将它们减少到 1 个提供程序,因为并非所有逻辑都将在一个提供程序和另一个提供程序中共享或共用

我使用了 Kent C. Dodds 的博客post“How to use React Context effectively”作为有效编写上下文提供程序的参考。

示例:(基本反例,但我将解释工作流程)

const MainContext = createContext(null);

const MyComponent = (props) => {
  const [counter, updateCounter] = useState(0);


  const increment = () => {
    updateCounter(counter + 1);
  }

  const decrement = () => {
   updateCounter(counter - 1);
  }

  return(
    <MainContext.Provider value={{counter, increment, decrement}}>
     {children}
   </MainContext.Provider>
  )
}

const useCountNumber = () => {
  const context = useContext(MainContext);
  
  if(context === undefined || context === null) {
    throw new Error('useCounter is not within MainContext scope');
  }
  else {
    return context.counter;
  }
}

const useIncrementCount = () => {

  const context = useContext(MainContext);
  
  if(context === undefined || context === null) {
    throw new Error('useIncrementCount is not within MainContext scope');
  }
  else {
    return context.increment;
  }
}

const useDecrementCount = () => {

  const context = useContext(MainContext);
  
  if(context === undefined || context === null) {
    throw new Error('useDecrementCount is not within MainContext scope');
  }
  else {
    return context.decrement;
  }

}



// in component you wish to use those values

const MyCounter = () => {

  const count = useCountNumber();
  const increment = useIncrementCount();
  const decrement = useDecrementCount();

  return(
   <div>
      {count}

      <button onClick={increment}> +1 </button>
      <button onClick={decrement}> -1 </button>
   </div>

);

}

我在生产中使用过它,使用一个上下文提供程序,然后将值放入该单个提供程序中。这对于一小组功能来说是可以管理的,但随着它变得更大,我会建议使用像 redux 或其他状态管理库这样的东西

还可以考虑使用 useMemo 来记忆某些状态元素,并考虑 useReducer 在触发深度更新时利用函数来优化上下文的性能

你可以使用这个 npm 包 react-pipeline-component

你的代码应该是这样的:

import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
import Home from "./pages/Home";
import Coaches from "./pages/Coaches";
import Athletes from "./pages/Athletes";
import Courts from "./pages/Courts";

import CoachState from "./context/coach/CoachState";
import AthleteState from "./context/athlete/AthleteState";
import CourtState from "./context/court/CourtState";

import {Pipeline, Pipe} from 'react-pipeline-component'

function App() {
    return (
        <Pipeline components={[
          <CourtState children={<Pipe />} />,
          <AthleteState children={<Pipe />} />,
          <CoachState children={<Pipe />} />,
          <Router children={<Pipe />} />,
          <Switch children={<Pipe />} />,
          <>
            <Route exact path="/" component={Home}></Route>
            <Route exact path="/coaches" component={Coaches}></Route>
            <Route exact path="/athletes" component={Athletes}></Route>
            <Route exact path="/courts" component={Courts}></Route>
          </>
        ]}/>
    );
}

export default App;