我对react钩子比较陌生,我正在尝试创建这个自定义钩子来处理我的API的CRUD操作。
这是钩子文件:
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;
然而,当我在useEffect()钩子中调用主组件中的api.list()时,我得到了一个无限循环。
示例组件调用:
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
谁能帮我理解这个问题?提前感谢!这就是导致无限循环的原因:
setApi({
...api,
list: list,
create: create
});
你不应该在渲染过程中调用setState()。
在你的情况下,你不需要为api对象useState
,你可以在每次渲染时返回它:
return {
isLoading,
apiData,
serverError,
api: { list, create }
};
这里是固定沙盒的链接
同时,另一个警告:此代码将重复调用api.list()
。
useEffect(() => {
api.list();
}, [api]);
由于api
在每次渲染时都会发生变化,因此它将重复调用api.list()
。
这是每次渲染时改变的对象:
return { isLoading, apiData, serverError, api };
你可以确保你只调用api.list()
一次,通过使用ref.
import { useRef } from 'react'
// In the component
const gotRef = useRef(false)
useEffect(() => {
if (!gotRef.current) {
api.list();
gotRef.current = true
}
}, [api]);