我为上下文创建了一个react
函数组件,如下所示:
const ItemContext = createContext()
const ItemProvider = (props) => {
const [item, setItem] = useState(null)
const findById = (args = {}) => {
fetch('http://....', { method: 'POST' })
.then((newItem) => {
setItem(newItem)
})
}
let value = {
actions: {
findById
},
state: {
item
}
}
return <ItemContext.Provider value={value}>
{props.children}
</ItemContext.Provider>
}
这样,我的上下文可以处理所有API调用并存储该item
的状态。 (类似于redux
等)
然后在我的子组件中使用上述上下文的那一行...
const smallComponent = () =>{
const {id } = useParams()
const itemContext = useContext(ItemContext)
useEffect(()=>{
itemContext.actions.findById(id)
},[id])
return <div>info here</div>
}
因此,组件应在id
更改时进行API调用。但是我在控制台中收到此错误:
React Hook useEffect缺少依赖项:'itemContext.actions'。要么包含它,要么删除依赖项数组react-hooks / exhaustive-deps
但是,如果我将其添加到依赖项数组中,则会在服务器上出现API调用永无休止的循环。所以我不确定该怎么办。或者,如果我走错路了。谢谢。
===更新==== 这是一个jsfiddle可以尝试:https://jsfiddle.net/zx5t76w2/ (仅供参考,我意识到警告不在控制台中,因为它没有掉毛)
答案 0 :(得分:2)
您可以仅将useCallback
用于获取方法,returns a memoized function:
const findById = useCallback((args = {}) => {
fetch("http://....", { method: "POST" }).then(newItem => {
setItem(newItem);
});
}, []);
...并放在useEffect
中:
...
const { actions, state } = useContext(ItemContext)
useEffect(() => {
actions.findById(id)
}, [id, actions.findById])
...
工作示例:https://jsfiddle.net/6r5jx1h7/1/
您的问题与useEffect
一次又一次调用您的自定义钩子有关,因为这是正常的功能,React不会在整个渲染过程中“保存”。
更新
我的最初答案解决了无限循环问题。
您的问题还与您使用上下文的方式有关,因为它一次又一次(See caveats in the official documentation)重新创建上下文的域对象(actions
,state
,..)。 / p>
以下是您在Kent C. Dodds'中将上下文划分为state
和dispatch
的绝佳方式的示例,我对此并不推荐。这将修复您的无限循环,并提供上下文使用情况的更清晰的结构。请注意,根据我的原始答案,我仍在使用useCallback
作为提取功能:
完整的代码沙箱https://codesandbox.io/s/fancy-sea-bw70b
App.js
import React, { useEffect, useCallback } from "react";
import "./styles.css";
import { useItemState, ItemProvider, useItemDispatch } from "./item-context";
const SmallComponent = () => {
const id = 5;
const { username } = useItemState();
const dispatch = useItemDispatch();
const fetchUsername = useCallback(async () => {
const response = await fetch(
"https://jsonplaceholder.typicode.com/users/" + id
);
const user = await response.json();
dispatch({ type: "setUsername", usernameUpdated: user.name });
}, [dispatch]);
useEffect(() => {
fetchUsername();
}, [fetchUsername]);
return (
<div>
<h4>Username from fetch:</h4>
<p>{username || "not set"}</p>
</div>
);
};
export default function App() {
return (
<div className="App">
<ItemProvider>
<SmallComponent />
</ItemProvider>
</div>
);
}
item-context.js
import React from "react";
const ItemStateContext = React.createContext();
const ItemDispatchContext = React.createContext();
function itemReducer(state, action) {
switch (action.type) {
case "setUsername": {
return { ...state, username: action.usernameUpdated };
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
}
}
function ItemProvider({ children }) {
const [state, dispatch] = React.useReducer(itemReducer, {
username: "initial username"
});
return (
<ItemStateContext.Provider value={state}>
<ItemDispatchContext.Provider value={dispatch}>
{children}
</ItemDispatchContext.Provider>
</ItemStateContext.Provider>
);
}
function useItemState() {
const context = React.useContext(ItemStateContext);
if (context === undefined) {
throw new Error("useItemState must be used within a CountProvider");
}
return context;
}
function useItemDispatch() {
const context = React.useContext(ItemDispatchContext);
if (context === undefined) {
throw new Error("useItemDispatch must be used within a CountProvider");
}
return context;
}
export { ItemProvider, useItemState, useItemDispatch };
当我开始使用带有钩子的上下文时,这两个博客文章都对我有很大帮助:
https://kentcdodds.com/blog/application-state-management-with-react https://kentcdodds.com/blog/how-to-use-react-context-effectively
答案 1 :(得分:0)
好吧,我不想写一个答案,因为Bennett基本上给了您解决方法,但是我认为它缺少组件中的该部分,所以您可以开始:
const ItemProvider = ({ children }) => {
const [item, setItem] = useState(null)
const findById = useCallback((args = {}) => {
fetch('http://....', { method: 'POST' }).then((newItem) => setItem(newItem))
}, []);
return (
<ItemContext.Provider value={{ actions: { findById }, state: { item } }}>
{children}
</ItemContext.Provider>
)
}
const smallComponent = () => {
const { id } = useParams()
const { actions } = useContext(ItemContext)
useEffect(() => {
itemContext.actions.findById(id)
}, [actions.findById, id])
return <div>info here</div>
}
根据评论内容的扩展,这里是有效的JSFiddle