1

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

4 回答 4

2

您可以通过添加第三个参数(选项)来实现这一点,如下所示:

const { data, error } = useSWR(
    `http://localhost:8000/api/admin/general/${id}`,
    fetcher,
    {
        revalidateOnFocus: false,
        revalidateIfStale: false,
        // revalidateOnReconnect: false, // personally, I didn't need this one
    }

  );

它基本上缓存数据并防止一旦有缓存重新验证。这是文档的链接。

或者,根据文档,您可以使用useSWRImmutable1.0 版开始,以实现与默认设置这些选项相同的结果。

import useSWRImmutable from "swr/immutable"

const { data, error } = useSWRImmutable(`http://localhost:8000/api/admin/general/${id}`, fetcher)
于 2021-10-11T07:42:43.510 回答
0

我遇到了同样的问题,这是我的第一种方法(当然数据与您的不同):

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, {
        headers: { Authorization: "Bearer " + session.user.servertoken },
      })
      .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已更改,因此在返回页面之前再次渲染,因此再次触发并再次adults设置 useSWR 并再次调用渲染,这将在无限循环中继续。为了解决这个问题,我决定仅在 takeData 为 true 且 takeData 仅在页面第一次渲染后才变为 true 时才发送请求(因为这就是在这种情况下要做useEffect的事情)。但它没有用,我不知道为什么。

所以我采用了第二种解决方案:

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, {
        headers: { Authorization: "Bearer " + session.user.servertoken },
      })
      .then((res) => res.data);

  const { data, error } = useSWR(
    takeData ? `http://localhost:8000/api/admin/general/${id}` : null,
    fetcher
  );

  
 useEffect(() => {
   if (data) {
     const adults = data.map((a) => a.adults);
     const reducer = (accumlator, item) => {
       return accumlator + item;
     };
     const totalAdults = adults.reduce(reducer, 0);
     setAdults(totalAdults);
   }
  }, []);
  

这里不是在第一次渲染后发出请求,而是立即完成请求,但在第一次渲染后检查数据。第二种解决方案对我有用。

如果有人知道为什么第一个解决方案不起作用,请告诉我;)

于 2021-08-02T14:32:54.533 回答
0

您应该使用取决于数据值的 useEffect 以便只有在渲染之间发生数据更改时才能更新它,接受的答案使用 useEffect 中的 useSWR 挂钩,这是不正确的

例子:

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
  );

  useEffect(() => {
   const adults = data.map((a) => a.adults);
     const reducer = (accumlator, item) => {
       return accumlator + item;
     };
     const totalAdults = adults.reduce(reducer, 0);
     setAdults(totalAdults);
  }, [data]);

}
于 2021-07-19T06:54:15.823 回答
-1

问题是您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);
}, []);

还要更新您的加载和错误检查。也许为两者创建一个新的状态变量。

于 2021-03-15T15:39:20.127 回答