Вы можете просто использовать useCallback
для метода выборки, который возвращает запомненную функцию:
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 не является "экономия" на протяжении всего рендера.
ОБНОВЛЕНИЕ
Мой первоначальный ответ исправил бесконечность l oop. Ваша проблема была также связана с тем, как вы используете контекст, так как он снова и снова воссоздает доменные объекты вашего контекста (actions
, state
, ..) ( См. Предупреждения в официальной документации ).
Вот ваш пример в Кент C. замечательный способ Доддса разделить контекст на state
и dispatch
, который я не могу рекомендовать достаточно. Это исправит ваш бесконечный l oop и обеспечит более чистую структуру использования контекста. Обратите внимание, что я все еще использую useCallback
для функции выборки, основываясь на моем исходном ответе:
Полные коды и окно https://codesandbox.io/s/fancy-sea-bw70b
Приложение. 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