#javascript #reactjs #react-hooks
Вопрос:
Я относительно новичок в крючках реагирования и пытаюсь создать этот пользовательский крючок для обработки операций CRUD для моего API.
Это файл с крючком:
import React, { useState, useEffect } from "react";
const useApi = (url, headers = { method: "GET" }, payload = null) => {
const [isLoading, setIsLoading] = useState(true);
const [apiData, setApiData] = useState(null);
const [serverError, setServerError] = useState(null);
const [api, setApi] = useState({});
const list = async () => {
try {
const resp = await fetch(url);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
const create = async () => {
try {
const resp = await fetch(url, (headers = { method: "POST" }), payload);
const data = await resp?.json();
setApiData(data);
setIsLoading(false);
} catch (error) {
setServerError(error);
} finally {
setIsLoading(false);
}
};
setApi({
...api,
list: list,
create: create
});
return { isLoading, apiData, serverError, api };
};
export default useApi;
Однако, когда я вызываю api.list() в своем основном компоненте внутри крючка useEffect (), я получаю бесконечный цикл.
Пример вызова компонента:
import { useEffect } from "react";
import useApi from "./useApi";
export default function App() {
const {
isLoading: loading,
apiData: students,
serverError: error,
api
} = useApi("https://59f0f160ce72350012bec011.mockapi.io/students");
console.log(loading, students, error, api);
useEffect(() => {
api.list();
}, [api]);
return (
<div className="App">
<h1>list</h1>
{loading ? "loading" : students.map((x) => x.name)}
</div>
);
}
Вот песочница для этого:
https://codesandbox.io/s/cocky-chebyshev-d9q89?file=/src/App.js:0-492
Кто-нибудь может помочь мне разобраться в этой проблеме? Заранее благодарю вас!
Ответ №1:
Это то, что вызывает бесконечный цикл:
setApi({
...api,
list: list,
create: create
});
Вы не должны вызывать setState() во время рендеринга.
В вашем случае вам не нужно useState
для объекта api, вы можете просто возвращать его при каждом рендеринге:
return {
isLoading,
apiData,
serverError,
api: { list, create }
};
Вот ссылка на исправленную песочницу
Кроме того, еще одно предупреждение: этот код будет повторно вызываться api.list()
.
useEffect(() => {
api.list();
}, [api]);
Поскольку api
изменения происходят при каждом рендеринге, он будет повторно вызываться api.list()
.
Это объект, который меняется при каждом рендеринге:
return { isLoading, apiData, serverError, api };
Вы можете убедиться, что звоните только api.list()
один раз, используя ссылку.
import { useRef } from 'react'
// In the component
const gotRef = useRef(false)
useEffect(() => {
if (!gotRef.current) {
api.list();
gotRef.current = true
}
}, [api]);
Комментарии:
1. Спасибо @programmerRaf. Я добавил ссылку, но она все равно дает мне бесконечные циклы. Вы можете ознакомиться с проблемой здесь: codesandbox.io/s/cocky-chebyshev-d9q89?file=/src/App.js
2. @rsilva Я нашел настоящую причину бесконечного цикла. Я отредактировал ответ.