0

我正在学习 React 并使用上下文构建一个项目。最初我在 App.js 组件中构建上下文,但我想将其分离到自己的文件中以保持 App.js 更干净。但是,我无法弄清楚这是如何完成的,或者是否可能。我认为我坚持的部分是如何提供上下文。我在下面粘贴了我的新 Context 文件以及 App.js,其中原始 Context 结构被注释掉了。有谁知道我怎么能做到这一点?

// TaskManipulatorContext
import React, { useReducer } from "react";
import AddTask from "../Components/dnd_components/AddTask";

export const TaskManipulatorContext = React.createContext();

const taskManipulatorInitialState = {
  panelData: {
    height: 50,
    open: false,
    title: "title",
    content: "content",
  },
  newTask: false,
  deleteTask: false,
  taskContainer: null,
};

const taskManipulatorReducer = (state, action) => {
  switch (action.type) {
    case "addTask-botPanel":
      // Brings up bottom panel to add new task
      return {
        ...state,
        panelData: {
          ...state.panelData,
          height: 20,
          open: true,
          title: "Add Task",
          content: <AddTask />,
        },
        taskContainer: {
          ...state.taskContainer,
          ...action.value,
        },
      };

    case "addTask-submitTask":
      // Submits task and adds it to list
      return {
        ...state,
        panelData: {
          ...state.panelData,
          closing: true,
        },
        newTask: true,
        taskContainer: {
          ...state.taskContainer,
          newTask: action.value,
        },
      };

    case "reset":
      // Reset back to initial state
      return taskManipulatorInitialState;
    default:
      return taskManipulatorInitialState;
  }
};

const [state, dispatch] = useReducer(
  taskManipulatorReducer,
  taskManipulatorInitialState
);

// App.js
import React, { useContext } from "react";
import "./index.css";
import RenderXMilageBoxes from "./Components/RenderXMilageBoxes";
import BottomPanel from "./Components/BottomPanel";
import AddTask from "./Components/dnd_components/AddTask";
import { TaskManipulatorContext } from "./Contexts/TaskManipulatorContext";

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * * CONTEXTS  * * * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
// export const TaskManipulatorContext = React.createContext();

// const taskManipulatorInitialState = {
//   panelData: {
//     height: 50,
//     open: false,
//     title: "title",
//     content: "content",
//   },
//   newTask: false,
//   deleteTask: false,
//   taskContainer: null,
// };

// const taskManipulatorReducer = (state, action) => {
//   switch (action.type) {
//     case "addTask-botPanel":
//       // Brings up bottom panel to add new task
//       return {
//         ...state,
//         panelData: {
//           ...state.panelData,
//           height: 20,
//           open: true,
//           title: "Add Task",
//           content: <AddTask />,
//         },
//         taskContainer: {
//           ...state.taskContainer,
//           ...action.value,
//         },
//       };

//     case "addTask-submitTask":
//       // Submits task and adds it to list
//       return {
//         ...state,
//         panelData: {
//           ...state.panelData,
//           closing: true,
//         },
//         newTask: true,
//         taskContainer: {
//           ...state.taskContainer,
//           newTask: action.value,
//         },
//       };

//     case "reset":
//       // Reset back to initial state
//       return taskManipulatorInitialState;
//     default:
//       return taskManipulatorInitialState;
//   }
// };

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 * * * * * * * * * * * * MAIN COMPONENT  * * * * * * * * * * * *
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
function App() {
  // Contexts
  // const [state, dispatch] = useReducer(
  //   taskManipulatorReducer,
  //   taskManipulatorInitialState
  // );

  const taskManipulatorContext = useContext(TaskManipulatorContext);

  return (
    <div>
      <taskManipulatorContext.Provider
        value={{
          state,
          dispatch,
        }}
      >
        <RenderXMilageBoxes currentMiles={5001} numFutureServices={5} />
        <BottomPanel panelData={state.panelData} />
      </taskManipulatorContext.Provider>
    </div>
  );
}

export default App;
4

1 回答 1

3

我首选的方法是创建一个上下文,然后为 Provider 和 useContext 钩子提供相同的上下文以在组件中使用。

它看起来像这样:

TaskManiupulatorContext.js

const TaskManipulatorContext = createContext({})

// dispatcher logic

export const TaskManipulatorContextProvider = () => {

    const [state, dispatch] = useReducer(
        taskManipulatorReducer,
        taskManipulatorInitialState
    );

    // You can choose to wrap this in a useMemo if you want to be extra careful about potential rerenders
    const taskManipulatorContextStore = {
        state,
        dispatch,
    }

    return <TaskManipulatorContext.Provider value={taskManipulatorContextStore}>{children}</TaskManipulatorContext.Provider>
}

export const useTaskManipulatorContext = () => useContext(TaskManipulatorContext)

然后通过使用提供者包装您的应用程序使上下文在应用程序级别可用:

应用程序.js

import { TaskManipulatorContextProvider } from './TaskManipulatorContext'

// app logic

const App = () => {
  return (
    <TaskManipulatorContextProvider>
      <App />
    </TaskManipulatorContextProvider>
  )
}

现在,您可以从 App 中的任何组件导入您的useTaskManipulatorContext钩子并使用它:

SomeComponent.js

import { useTaskManipulatorContext } from './TaskManipulatorContext'

export const SomeComponent = () => {

  const taskManipulatorCtx = useTaskManipulatorContext()

  const someFunctionWhichUpdatesContextState = (action) => {
    taskManipulatorCtx.dispatch(action)
  }

  return (
    <div>
      <text>{'Here is some state from the context'}</text>
      <text>{taskManipulatorCtx.state}</text>
    </div>
  )
}

请注意,采用这种方法可以让您对 Context 内部的访问模式进行大量控制。例如,如果您不希望组件直接访问调度以更改状态,则可以在 Provider 中创建一个方法来调度某些操作,并将其导出为您的函数可以使用的方法(即。您不依赖您的组件或使用您的组件的开发人员来了解如何更新状态的确切行为/实现)。

const taskManipulatorContextStore = {
  state,
  addTask: (taskToAdd) = dispatch(actions.add, taskToAdd)
}
于 2020-06-13T22:27:16.867 回答