问题描述
我正在使用 useSWR 获取数据,然后使用数据,我想通过使用 reduce 来获得总数。如果我使用 console.log 输出值,它工作正常,但是一旦我尝试使用该值设置状态,我就会收到“Too may re-renders”消息。
import Admin from "../../../components/admin/Admin";
import { useRouter } from "next/router";
import styles from "../../../styles/Dashboard.module.css";
import { getSession,useSession } from "next-auth/client";
import { useState } from "react";
/* BOOTSTRAP */
import Col from "react-bootstrap/Col";
import Container from "react-bootstrap/Container";
import Row from "react-bootstrap/Row";
import Spinner from "react-bootstrap/Spinner";
import Table from "react-bootstrap/Table";
import useSWR from "swr";
import axios from "axios";
const General = () => {
const [session,loading] = useSession();
const [adults,setAdults] = useState(null);
const router = useRouter();
const { id } = router.query;
const fetcher = (url) =>
axios
.get(url,{
headers: { Authorization: "Bearer " + session.user.servertoken },})
.then((res) => res.data);
const { data,error } = useSWR(
`http://localhost:8000/api/admin/general/${id}`,fetcher
);
if (data) {
const adults = data.map((a) => a.adults);
const reducer = (accumlator,item) => {
return accumlator + item;
};
const totalAdults = adults.reduce(reducer,0);
setAdults(totalAdults);
}
return (
<Admin>
<div className={styles.admin_banner}>
<Container fluid>
<Row>
<Col>
<h2>Bookings</h2>
<h6>
{adults}
</h6>
</Col>
</Row>
</Container>
</div>
<Container fluid>
<Row>
<Col>
<div className={styles.admin_container}>
{!error && !data && (
<Spinner animation="border" role="status">
<span className="sr-only">Loading...</span>
</Spinner>
)}
{!error && data && (
<Table responsive="md">
<thead>
<tr>
<th>Name</th>
</tr>
</thead>
<tbody>
{data &&
!error &&
data.map((d) => (
<tr key={d._id}>
<td>
{d.firstName} {d.lastName}
</td>
</tr>
))}
</tbody>
</Table>
)}
</div>
</Col>
</Row>
</Container>
</Admin>
);
};
export async function getServerSideProps(context) {
const session = await getSession({
req: context.req,});
if (!session) {
return {
redirect: {
destination: "/admin",permanent: false,},};
} else {
return { props: session };
}
}
export default General;
解决方法
问题是您的数据检查中的 setAdults(totalAdults);
导致无限循环。 React 中的钩子导致组件重新渲染,所以实际发生的是
data
为真 --> setAdults
被触发 --> 重新渲染 --> data
为真 --> ...
将您的逻辑移动到 useEffect 钩子中:
React.useEffect(() => {
const fetcher = url =>
axios
.get(url,{
headers: { Authorization: "Bearer " + session.user.servertoken },})
.then(res => {
const adults = data.map(a => a.adults);
const reducer = (accumlator,item) => {
return accumlator + item;
};
const totalAdults = adults.reduce(reducer,0);
setAdults(totalAdults);
});
useSWR(`http://localhost:8000/api/admin/general/${id}`,fetcher);
},[]);
还要更新您的加载和错误检查。也许为两者创建一个新的状态变量。
,我遇到了同样的问题,这是我的第一种方法(当然与您的数据不同):
const General = () => {
const [session,loading] = useSession();
const [adults,setAdults] = useState(null);
const router = useRouter();
const { id } = router.query;
const [takeData,setTakeData] = useState(false)
const fetcher = (url) =>
axios
.get(url,})
.then((res) => res.data);
const { data,error } = useSWR(
takeData ? `http://localhost:8000/api/admin/general/${id}` : null,fetcher
);
useEffect(() => {
setTakeData(true)
},[])
if (data) {
const adults = data.map((a) => a.adults);
const reducer = (accumlator,item) => {
return accumlator + item;
};
const totalAdults = adults.reduce(reducer,0);
setAdults(totalAdults);
}
问题在于,一旦状态发生变化,就会发生重新渲染。
在这种情况下,useSWR 返回数据(可能)并在第一次呈现页面之前更改 setAdults
的状态。此时 adults
已更改,因此在返回页面之前再次渲染,因此再次触发 useSWR 并再次设置 adults
并再次调用渲染,这将继续无限循环。
为了解决这个问题,我决定只在 takeData 为真时发送请求,而 takeData 仅在第一次呈现页面后才变为真(因为在这种情况下这就是 useEffect
要做的)。但它不起作用,我不知道为什么。
所以我采用了第二种方案:
const General = () => {
const [session,fetcher
);
useEffect(() => {
if (data) {
const adults = data.map((a) => a.adults);
const reducer = (accumlator,0);
setAdults(totalAdults);
}
},[]);
这里不是在第一次渲染后发出请求,而是立即完成请求,但在第一次渲染后检查数据。 第二种解决方案对我有用。
如果有人知道为什么第一个解决方案不起作用,请告诉我;)
,您应该使用依赖于数据值的 useEffect 以便只有在渲染之间数据发生更改时才可以更新它,接受的答案使用了 useEffect 中的 useSWR 钩子,这是不正确的
示例:
const General = () => {
const [session,setAdults] = useState(null);
const router = useRouter();
const { id } = router.query;
const fetcher = (url) =>
axios
.get(url,error } = useSWR(
`http://localhost:8000/api/admin/general/${id}`,fetcher
);
useEffect(() => {
const adults = data.map((a) => a.adults);
const reducer = (accumlator,0);
setAdults(totalAdults);
},[data]);
}