react自定义钩子导致无限循环



我对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]);

最新更新