typescript - 在获取数据后,ReactJS context-api 无法渲染
这是一个 next.js 网站,由于我的 Navbar 组件和购物车页面都应该能够访问我的购物车内容,所以我为它们创建了一个上下文。如果我尝试渲染页面,我会得到:
未处理的运行时错误:
类型错误:无法读取未定义的属性(读取 'key') :
注意:cartContent 数组存在且长度为 1,我可以通过使用 setTimeout 延迟数据渲染来获取它,但无法在数据获取后立即渲染它。
我需要在从 Firebase 返回数据后进行渲染,但总是遇到提到的错误。
这是我的 _app.tsx 文件
function MyApp({ Component, pageProps }) {
// set user for context
const userContext = startContext();
return (
<UserContext.Provider value = { userContext }>
<Navbar />
<Component {...pageProps} />
<Toaster />
</UserContext.Provider>
);
}
export default MyApp
此文件包含 startContext 函数,该函数返回上下文,以便可以使用它。
export const startContext = () => {
const [user] = useAuthState(auth);
const [cart, setCart] = useState(null);
const [cartContent, setCartContent] = useState(null);
useEffect(() => {
if (!user) {
setCart(null);
setCartContent(null);
}
else {
getCart(user, setCart, setCartContent);
}
}, [user]);
return { user, cart, setCart, cartContent, setCartContent };
}
此文件包含 getCart 函数。
export const getCart = async (user, setCart, setCartContent) => {
if (user) {
try {
let new_cart = await (await getDoc(doc(firestore, 'carts', user.uid))).data();
if (new_cart) {
let new_cartContent = []
await Object.keys(new_cart).map(async (key) => {
new_cartContent.push({...(await getDoc(doc(firestore, 'products-cart', key))).data(), key: key});
});
console.log(new_cartContent);
setCartContent(new_cartContent);
console.log(new_cartContent);
setCart(new_cart);
}
else {
setCart(null);
setCartContent(null);
}
} catch (err) {
console.error(err);
}
}
}
这是 cart.tsx 网页。当我加载它时,会出现所提到的错误。
export default () => {
const { user, cart, cartContent } = useContext(UserContext);
return (
<AuthCheck>
<div className="grid grid-cols-1 gap-4">
{cartContent && cartContent[0].key}
</div>
</AuthCheck>
)
}
我尝试过用多种不同的方式来渲染 cart 的内容[0].key,但都没有成功。总是出现好像它未定义的错误。使用 setTimeout 的方法可以解决,但我真的希望以一种体面的方式来解决这个问题,这样至少在不依赖 Firebase 的响应时间或网络延迟的情况下不会出错。
编辑:由于它可以与 setTimeout 一起使用,这感觉像是一个竞争条件,如果使用 setCartContent,它会触发重新渲染,但 setCartContent 在内容被渲染之前无法完成,因此它会将状态 cartContent 视为未定义,并且以后不会再次触发。
答案#1
试着改变
{cartContent && cartContent[0].key}
到
{cartContent?.length > 0 && cartContent[0].key}
编辑::实际问题出现在 getCart 函数的那一行
let new_cart = await (await getDoc(doc(firestore, 'carts', user.uid))).data();
这要么设置为空数组,要么设置为空对象。所以尝试将你的 if (new_cart) 条件更改为
if (Object.keys(new_cart).length > 0) {
现在你不会再得到undefined错误
答案#2
由于似乎存在竞争条件,我认为 setCartContent 在其内容被获取之前就执行了。所以我在 getCart 函数中将 map 循环改为使用异步函数的 for 循环
await Object.keys(new_cart).map(async (key) => {
new_cartContent.push({...(await getDoc(doc(firestore, 'products-cart', key))).data(), key: key});
});
到
for (const key of Object.keys(new_cart)) {
new_cartContent.push({...(await getDoc(doc(firestore, 'products-cart', key))).data(), key: key});
}
我不能在 map 函数中使用 await 而不让它变成异步,所以我用了 for 循环才让它工作。希望有人能找到解决这个问题的其他方法,我只能想出用 for 循环,所以代码是同步的。
