在页面之间快速导航时,Next.js 页面呈现错误数据的问题

问题描述 投票:0回答:1

我在应用程序的 Next Js 页面上呈现数据时遇到了一个特殊问题。这是场景,

我有一个 Next Js 页面,它根据查询参数 cid 显示客户信息。该页面使用 Axios 从 API 获取客户数据和相关组数据,然后将这些数据呈现在页面上。

当我在不同的客户 ID 之间快速导航时(cid=1、cid=2 等) 时,就会出现问题。有时,当我在页面之间快速切换时,页面会呈现错误的cid数据。例如,当我从 cid=2 导航到 cid=1 时,页面可能仍会呈现 cid=2 而不是 cid=1 的数据。

我尝试通过确保

fetchData
函数以正确的顺序执行并使用 router.query.cid 的最新值来调试此问题。然而问题依然存在。

这是我的代码的简化版本:

import { config } from '@/lib/headerConfig';
import axios from 'axios';
import dynamic from 'next/dynamic';
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useEffect, useState } from 'react';

// auth
import { getSession } from 'next-auth/react';

// components
const CustomersLayout = dynamic(() => import('@/components/customer/layout/Index'));
const Spinner = dynamic(() => import('@/components/global/Spinner'));

// zustand
import { useCustomersStore } from '@/middleware/zustand/customers';

const CustomerInformationPage = (props) => {
    const router = useRouter();
    const { customers, setCustomers, setPreviousPage } = useCustomersStore();
    const [customer, setCustomer] = useState({});
    const [groups, setGroups] = useState([]);

    useEffect(() => {
        setCustomers(props.customers);
        setPreviousPage(props.previousPage);
    }, [props.customers, props.previousPage]);

    useEffect(() => {
        const fetchData = async () => {
            try {
                if (router.query.cid) {
                    const customerData = await fetchCustomer(router.query.cid);
                    const groupsData = await fetchGroups(router.query.cid);
                    setCustomer(customerData);
                    setGroups(groupsData);
                }
            } catch (error) {
                console.error('Error fetching customer or groups:', error);
            }
        };

        fetchData();
    }, [router.query.cid]);

    const fetchCustomer = async (cid) => {
        const controller = new AbortController();
        const { signal } = controller;
        try {
            const res = await axios.get(`/api/customer?cid=${cid}`, { signal, ...config });
            return res.data[0];
        } catch (err) {
            console.error(err);
            throw err;
        } finally {
            controller.abort();
        }
    };

    const fetchGroups = async (cid) => {
        const controller = new AbortController();
        const { signal } = controller;
        try {
            const res = await axios.get(`/api/customer/groups?cid=${cid}`, { signal, ...config });
            return res.data;
        } catch (error) {
            console.error(error);
            throw error;
        } finally {
            controller.abort();
        }
    };

    if (customers.length < 1) {
        return <Spinner />;
    }
    return (
        <>
            <Head>
                <title>Customer Information</title>
            </Head>

            <CustomersLayout>
                {console.log({
                    customerID: customer.CustomerID,
                    routerQueryCid: router.query.cid,
                })}
                <>
                    <p>
                        {customer && customer.CompanyName}
                        <br />

                        {groups.length > 0 && groups[0].ProjectGroupName}
                    </p>
                </>
            </CustomersLayout>
        </>
    );
};

export const getServerSideProps = async (ctx) => {
    const session = await getSession(ctx);
    const { company_name } = ctx.query;

    const headers = ctx.req.headers;
    try {
        if (session && session.user.Permissions['Customer-Customer Information'].PermissionLevel > 1) {
            const customers = (
                await axios.get(`${process.env.NEXTAUTH_URL}/api/customers`, {
                    headers: {
                        Cookie: headers.cookie,
                        authorization: process.env.HEADER,
                    },
                })
            ).data;
            const previousPage = ctx.req.headers.referer || '';
            return {
                props: {
                    customers,
                    previousPage,
                },
            };
        } else if (session && session.user.Permissions['Customer-Customer Information'].PermissionLevel < 2) {
            return {
                redirect: {
                    destination: `/${company_name}/error/403`,
                    permanent: false,
                },
            };
        } else {
            return {
                redirect: {
                    destination: `/login?next=${ctx.req.url}`,
                    permanent: false,
                },
            };
        }
    } catch {
        return {
            redirect: {
                destination: `/${company_name}/error/403`,
                permanent: false,
            },
        };
    }
};

export default CustomerInformationPage;

我怀疑这个问题可能与异步数据获取和状态管理有关,但我不知道如何解决它。

任何人都可以深入了解为什么会出现此问题,并提出潜在的解决方案以确保为每个 cid 呈现正确的数据吗?

任何帮助或建议将不胜感激。谢谢!

asynchronous next.js react-hooks async-await
1个回答
0
投票

我建议以这样的方式实现中止控制器,即允许您在组件卸载或以其他方式使用新 id 值重新运行效果时取消正在进行的请求。

示例:

const CustomerInformationPage = (props) => {
  const router = useRouter();
  const { customers, setCustomers, setPreviousPage } = useCustomersStore();

  const [customer, setCustomer] = useState({});
  const [groups, setGroups] = useState([]);
  const [isLoading, setIsLoading] = useState();

  useEffect(() => {
    setCustomers(props.customers);
    setPreviousPage(props.previousPage);
  }, [props.customers, props.previousPage]);

  useEffect(() => {
    const controller = new AbortController();
    const { signal } = controller;

    const fetchCustomer = async (cid) => {
      try {
        const { data } = await axios.get(
          `/api/customer?cid=${cid}`,
          { signal, ...config }
        );
        return data[0];
      } catch (err) {
        console.error(err);
        throw err;
      }
    };

    const fetchGroups = async (cid) => {
      try {
        const { data } = await axios.get(
          `/api/customer/groups?cid=${cid}`,
          { signal, ...config }
        );
        return data;
      } catch (error) {
        console.error(error);
        throw error;
      }
    };

    const fetchData = async () => {
      if (!router.query.cid) {
       return;
      }

      try {
        setIsLoading(true);
        const [customerData, groupsData] = await Promise.all([
          fetchCustomer(router.query.cid),
          fetchGroups(router.query.cid)
        ]);
        setCustomer(customerData);
        setGroups(groupsData);
      } catch (error) {
        console.error('Error fetching customer or groups:', error);
      } finally {
        setIsLoading(false);
      }
    };

    fetchData();

    return () => {
      controller.abort();
    };
  }, [router.query.cid]);

  if (isLoading || !customers.length) {
    return <Spinner />;
  }

  return (
    <>
      <Head>
        <title>Customer Information</title>
      </Head>

      <CustomersLayout>
        <p>
          {customer?.CompanyName}
          <br />

          {groups[0]?.ProjectGroupName}
        </p>
      </CustomersLayout>
    </>
  );
};
© www.soinside.com 2019 - 2024. All rights reserved.