【发布时间】:2021-09-24 15:39:06
【问题描述】:
我对上下文 API 还很陌生,并且对 useState 和 useEffect 之外的钩子做出反应,所以请与我交流。
我正在尝试创建一个自定义 useGet 钩子,我可以使用它从后端获取一些数据,然后使用上下文 API 存储它,这样如果我在应用程序的其他地方再次使用相同上下文的 Get,它可以首先检查数据是否已被检索并节省一些时间和资源必须执行另一个 GET 请求。我正在尝试将其编写为通常与各种不同的数据和上下文一起使用。
我已经完成了大部分工作,直到我尝试将数据发送到 useReducer 状态,然后我收到错误:
Hooks can only be called inside the body of a function component.
我知道我的调度调用可能违反了挂钩规则,但我不明白为什么只有一个调用会引发错误,或者如何修复它以完成我需要的操作。任何帮助将不胜感激。
commandsContext.js
import React, { useReducer, useContext } from "react";
const CommandsState = React.createContext({});
const CommandsDispatch = React.createContext(null);
function CommandsContextProvider({ children }) {
const [state, dispatch] = useReducer({});
return (
<CommandsState.Provider value={state}>
<CommandsDispatch.Provider value={dispatch}>
{children}
</CommandsDispatch.Provider>
</CommandsState.Provider>
);
}
function useCommandsState() {
const context = useContext(CommandsState);
if (context === undefined) {
throw new Error("Must be within CommandsState.Provider");
}
return context;
}
function useCommandsDispatch() {
const context = useContext(CommandsDispatch);
if (context === undefined) {
throw new Error("Must be within CommandsDispatch.Provider");
}
return context;
}
export { CommandsContextProvider, useCommandsState, useCommandsDispatch };
使用Get.js
import { API } from "aws-amplify";
import { useRef, useEffect, useReducer } from "react";
export default function useGet(url, useContextState, useContextDispatch) {
const stateRef = useRef(useContextState);
const dispatchRef = useRef(useContextDispatch);
const initialState = {
status: "idle",
error: null,
data: [],
};
const [state, dispatch] = useReducer((state, action) => {
switch (action.type) {
case "FETCHING":
return { ...initialState, status: "fetching" };
case "FETCHED":
return { ...initialState, status: "fetched", data: action.payload };
case "ERROR":
return { ...initialState, status: "error", error: action.payload };
default:
return state;
}
}, initialState);
useEffect(() => {
if (!url) return;
const getData = async () => {
dispatch({ type: "FETCHING" });
if (stateRef.current[url]) { // < Why doesn't this also cause an error
const data = stateRef.current[url];
dispatch({ type: "FETCHED", payload: data });
} else {
try {
const response = await API.get("talkbackBE", url);
dispatchRef.current({ url: response }); // < This causes the error
dispatch({ type: "FETCHED", payload: response });
} catch (error) {
dispatch({ type: "ERROR", payload: error.message });
}
}
};
getData();
}, [url]);
return state;
}
编辑 --
useCommandsState 和 useCommandsDispatch 被导入到我调用 useGet 传递下来的这个组件中。
import {
useCommandsState,
useCommandsDispatch,
} from "../../contexts/commandsContext.js";
export default function General({ userId }) {
const commands = useGet(
"/commands?userId=" + userId,
useCommandsState,
useCommandsDispatch
);
为什么我只收到 dispatchRef.current 的错误,而不是 stateRef.current,当它们都对 useReducer 的 state/dispatch 做完全相同的事情时?
如何重构它来解决我的问题?总而言之,我需要能够在每个上下文的两个或多个位置调用 useGet,第一次调用存储在传递的上下文中的数据。
以下是我一直在阅读的内容的各种链接,这些链接帮助我走到了这一步。
How to combine custom hook for data fetching and context?
Updating useReducer 'state' using useEffect
【问题讨论】:
-
它们作为 useContextState 和 useContextDispatch 传递给 useGet。像这样命名以便它们可以被普遍使用,它只是在这种情况下被用于 CommandsContext
-
我已将其编辑到我的问题中。
-
我需要在不同嵌套级别的大约 3 个组件之间共享状态,并且不想为每个组件执行 GET 请求。
标签: reactjs react-hooks react-context