Основываясь на упрощенном примере, который вы предоставили в ссылке, проблема заключается в useEffect()
в реализации useFetch()
, которая безоговорочно вызывает fetchData()
после первого рендеринга.Учитывая ваш вариант использования, вам не нужно такое поведение, так как вы вызываете его вручную на основе перечисленных вами условий.
В дополнение к этому вы обострили проблему, вызвав companiesFetch()
непосредственно в функциональном компоненте, чего вы не должны делать, потому что fetchData()
делает синхронные вызовы setState()
и изменяет состояние вашего компонента во время рендеринга.
Вы можете решить эти проблемы, сначала изменив useFetch()
удалить безусловную выборку после первого рендеринга, а затем путем перемещения вашей условной логики в useEffect()
обратный вызов внутри компонента.Вот как я бы реализовал useFetch()
:
const initialFetchState = { isLoading: false, isError: false };
// automatically merge update objects into state
const useLegacyState = initialState => useReducer(
(state, update) => ({ ...state, ...update }),
initialState
);
export const useFetch = config => {
const instance = useMemo(() => axios.create(config), [config]);
const [state, setState] = useLegacyState(initialFetchState);
const latestRef = useRef();
const fetch = useCallback(async (...args) => {
try {
// keep ref for most recent call to fetch()
const current = latestRef.current = Symbol();
setState({ isError: false, isLoading: true });
const { data } = await instance.get(...args).finally(() => {
// cancel if fetch() was called again before this settled
if (current !== latestRef.current) {
return Promise.reject(new Error('cancelled by later call'));
}
});
setState({ data });
} catch (error) {
if (current === latestRef.current) {
setState({ isError: true });
}
} finally {
if (current === latestRef.current) {
setState({ isLoading: false });
}
}
}, [instance]);
return [state, fetch];
};
, и вот как я бы назвал его в useEffect()
, чтобы предотвратить синхронные вызовы на setState()
:
const [companies, companiesFetch] = useFetch({ baseURL: API_ROOT() });
const [newUsers, setNewUsers] = useState({ companies: [] });
useEffect(() => {
if (!companies.isLoading) {
if (appStore.currentAccessLevel === 99 && newUsers.companies.length === 0) {
console.log('About to call companiesFetch');
companiesFetch('acct_mgmt/companies');
} else if (companies.status === 200 && newUsers.companies !== companies.data.companies) {
setNewUsers({ companies: companies.data.companies });
}
}
}, [appStore.currentAccessLevel, companies, newUsers]);
Inв вашем примере я не уверен, в какой области был объявлен newUsers
, но просмотр newUsers.companies = ...
в обратном вызове useEffect()
вызвал некоторую обеспокоенность, поскольку подразумевается, что ваш функциональный компонент нечист, что может легко привести к незначительным ошибкам..