typescript - ReactJS context-api kommer inte att rendera efter att jag har hämtat data
Detta är en next.js-sajt, eftersom både min Navbar-komponent och min kundvagnssida bör ha tillgång till innehållet i min kundvagn skapade jag en kontext för dem. Om jag försöker rendera sidan får jag:
Ohanterat körfel :
TypeError: Kan inte läsa egenskaper av undefined (läser 'key') :
obs: cartContent-arrayen finns och har längd 1, jag kan få den genom att fördröja när datan renderas med hjälp av setTimeout, men kan inte få den att renderas direkt efter att den hämtats.
Jag behöver få det att renderas efter att datan från Firebase har returnerats, men stöter alltid på det nämnda felet.
Detta är min _app.tsx-fil
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
Denna fil har startContext-funktionen som returnerar kontexten så att den kan användas.
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 };
}
Denna fil innehåller funktionen 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);
}
}
}
Detta är webbplatsen cart.tsx. När jag laddar den får jag det nämnda felet.
export default () => {
const { user, cart, cartContent } = useContext(UserContext);
return (
<AuthCheck>
<div className="grid grid-cols-1 gap-4">
{cartContent && cartContent[0].key}
</div>
</AuthCheck>
)
}
Jag har försökt att återge cart's content[0].key på många olika sätt, men kunde inte göra det. Får alltid ett fel som om det vore odefinierat. Att använda en setTimeout-lösning fungerar, men jag ville verkligen lösa detta på ett ordentligt sätt så att det åtminstone är felfritt i bemärkelsen att det inte beror på firebases svarstid/internetlatens.
Redigera: Eftersom det fungerar med setTimeout känns det som ett race condition där om setCartContent används, triggar det omrenderingen men setCartContent kan inte slutföras innan saker renderas så det kommer att betrakta tillståndet cartContent som undefined och kommer inte att trigga igen senare.
Svar #1
Försök ändra
{cartContent && cartContent[0].key}
till
{cartContent?.length > 0 && cartContent[0].key}
Redigera:: Det verkliga problemet är i getCart-funktionen på raden
let new_cart = await (await getDoc(doc(firestore, 'carts', user.uid))).data();
Det här är antingen inställt på en tom array eller ett tomt objekt. Så försök att ändra ditt if (new_cart)-villkor till
if (Object.keys(new_cart).length > 0) {
Nu kommer du inte få undefined-felet
Svar #2
Eftersom det verkade vara ett tävlingsförhållande, antog jag att setCartContent kördes innan dess innehåll hämtades. Så jag ändrade i getCart-funktionen map-loopen med en asynkron funktion till en for-loop
await Object.keys(new_cart).map(async (key) => {
new_cartContent.push({...(await getDoc(doc(firestore, 'products-cart', key))).data(), key: key});
});
till
for (const key of Object.keys(new_cart)) {
new_cartContent.push({...(await getDoc(doc(firestore, 'products-cart', key))).data(), key: key});
}
Jag kan inte skapa en map-funktion med await i utan att göra den asynkron, så jag använde for-loopen som gjorde att det fungerade. Hoppas att någon hittar några alternativ för att lösa detta, jag kunde bara komma på en for-loop så koden är synkron.
