微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

设置状态时重新渲染太多 - useSWR

如何解决设置状态时重新渲染太多 - useSWR

我正在使用 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]);

}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?