我在应用程序的 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 呈现正确的数据吗?
任何帮助或建议将不胜感激。谢谢!
我建议以这样的方式实现中止控制器,即允许您在组件卸载或以其他方式使用新 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>
</>
);
};