【问题标题】:ReactJS context-api won't render after I get data获取数据后,ReactJS context-api 不会呈现
【发布时间】:2022-01-05 05:31:46
【问题描述】:

这是一个 next.js 站点,因为我的导航栏组件和我的购物车页面都应该可以访问我的购物车内容,所以我为它们创建了一个上下文。如果我尝试渲染页面,我会得到:

未处理的运行时错误

TypeError: Cannot read properties of undefined (reading 'key')

obs: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>
    )
}

我尝试以多种不同的方式呈现购物车的 content[0].key,但都做不到。总是得到错误,好像它是未定义的。做一个 setTimeout hack 是可行的,但是,我真的很想以一种体面的方式解决这个问题,所以它至少在不依赖于 firebase 的响应时间/互联网延迟的意义上是防错的。

编辑: 由于它与 setTimeout 一起使用,因此感觉就像一个竞争条件,如果使用 setCartContent,它会触发重新渲染,但 setCartContent 无法在渲染内容之前完成,因此它会将状态 cartContent 视为未定义,以后不会再次触发。

【问题讨论】:

标签: reactjs typescript next.js react-context


【解决方案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 错误

【讨论】:

  • 我刚试过这个:{cartContent?.length &gt; 0 &amp;&amp; cartContent[0].key}{cartContent?.length &gt; 0 &amp;&amp; &lt;p&gt;test&lt;/p&gt;} 没有工作,它似乎在组件未定义时呈现,然后将组件更新为其值(长度为 1 的数组)重新渲染
  • 由于它与 setTimeout 一起使用,感觉就像一个竞争条件,如果使用 setCartContent,它会触发重新渲染,但 setCartContent 无法在渲染内容之前完成,因此以后不会再次触发它不会渲染任何东西。
  • @DouweDeJong 检查我在答案中的编辑
  • 这仍然不起作用。如果我在 getCart 函数中的条件之后使用 console.log(new_cart),我会得到正确的数据(一个带有单个键的对象),但它不会呈现内容。正如我所说,我认为它会在设置状态之前触发渲染。
【解决方案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});
}

如果不使其异步,我无法在其中创建带有 await 的 map 函数,因此我使用 for 循环使其工作。希望有人找到解决这个问题的替代方法,我只能想出一个 for 循环,所以代码是同步的。

【讨论】:

    猜你喜欢
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 2021-09-06
    • 2018-01-01
    • 2021-05-22
    • 1970-01-01
    • 2019-07-10
    • 1970-01-01
    相关资源
    最近更新 更多