如何在 Next.js 13 的客户端组件中使用 cookie,并在启用动态路由时在初始 SSR 请求中读取它们?

Len*_*ter 15 cookies next.js next.js13

我真的不明白 Next.js 有什么计划让 cookie 在客户端组件中工作。他们当前的 cookie 文档与客户端组件不兼容。

在服务器组件中,您可以使用服务器操作读取和设置 cookie 。对于客户端组件,您可以使用服务器操作来设置它们,但看起来您无法读取cookie。尝试使用 cookies().get(name) 会导致以下错误。

您正在导入需要 next/headers 的组件。这只适用于服务器

...

组件,但其父组件之一标记为“使用客户端”,因此它是客户端组件。其中之一被标记为带有“use client”的客户端条目:
./app/client/hooks/useCookies.ts
./app/client/components/SomeComponent.tsx

当然,您可以添加服务器操作来读取 cookie,我已经测试过它是否有效。但我拒绝用异步函数处理和不必要的服务器行程来搞乱我的代码,只是为了读取浏览器中已经存在的 cookie。

我还尝试过库,例如cookies-next,它似乎是 Next.js cookies 的最佳维护和工作库。它的作用是在客户端的客户端组件中读取和设置 cookie。

使用该库的问题是初始渲染闪烁,因为该库似乎无法通过应用程序路由读取 cookie 服务器端。即使我使用路由段配置手动启用动态路由,所有 cookie 在初始 SSR 请求中都未定义。当我到达客户端时,首先找到并正确读取 cookie。而且静态渲染的话,cookie显然是无法读取的。

我还尝试使用内置 cookie 系统的 Next.js 设置 cookie,然后使用客户端组件中的 cookies-next 库读取它们。即使使用该解决方案,初始 SSR 请求中的 cookie 也未定义。

因此我想知道,谁能告诉我在使用 Next.js 应用程序路由时是否有办法使这两个条件一起工作?

  • 能够直接从浏览器读取客户端组件中的 cookie(无服务器操作)
  • 当启用动态路由时,能够在初始 SSR 请求中读取客户端组件中的 cookie

我真的必须读取服务器组件中的 cookie 并将 cookie 值作为序列化值传递给客户端组件吗?这也意味着当我为 cookie 设置新值时它们不会自动更新。

我觉得这里有一些东西非常不对劲,或者我希望我完全错过了一些东西。cookie 的全部目的是您应该能够在客户端和服务器端读取它们。

teo*_*eos 5

关于客户端组件的一个常见困惑是它们也在服务器上呈现(初始化时一次),并作为纯 HTML/CSS 发送回来。
它们稍后会在浏览器中水合以添加 JavaScript。

请参阅Next.js - 什么是流式传输?

此时,像cookies-next或 之类的javascript 库js-cookie将不会按您的预期运行,因为它们实际上并未在您的浏览器中运行(没有windowdocument,我们是服务器端)。

这意味着客户端组件中的第一次渲染可能不完整,并且它不会加载您期望的数据。
在大多数情况下这没什么问题,因为它只持续很短的时间。这个想法只是提供尽可能最好的静态占位符/外壳,但在您的情况下,这就是导致闪烁效果的原因

您可以通过在浏览器中禁用 javascrit 来亲自观察。您将看到所有客户端组件均已加载,但可能未达到您预期的状态。

简而言之:

  • cookies-next客户端组件可以通过(不是通过)访问文档cookie next/headers,但第一个(静态)渲染不会从中获取任何数据。
  • 另一方面,服务器组件可以通过next/headers(但不能通过cookies-next)访问 HTTP cookie。

你不能在同一个组件中同时拥有两种行为,
我相信这是设计使然,一个函数无法在两个空间中工作。


但是,您可以构建一个客户端组件并将其包装到服务器组件中,如下所示:

页面.tsx

import { cookies } from 'next/headers'
import { MyClientComponent } from './MyClientComponent'

export default function Page() {
    // Server-side: based on HTTP resquest cookie only
    const bgColor = cookies().get('bgColor')?.value
    return <MyClientComponent initial={{ bgColor }} />
}
Run Code Online (Sandbox Code Playgroud)

MyClientComponent.tsx

'use client'

import { getCookie, setCookie } from 'cookies-next'
import { useState, useEffect } from 'react'

export function MyClientComponent({
    initial,
}: {
    initial: { bgColor?: string }
}) {
    const [bgColor, setBgColor] = useState(
        getCookie('bgColor')?.toString() ?? initial.bgColor ?? '',
    )

    useEffect(() => {
        setCookie('bgColor', bgColor)
    }, [bgColor])

    return (
        <input
            style={{ backgroundColor: bgColor }}
            value={bgColor}
            onChange={({ target }) => {
                setBgColor(target.value)
            }}
        />
    )
}

Run Code Online (Sandbox Code Playgroud)

还:

  • 始终使用Link,以便页面在导航时不会刷新,仅将闪烁效果限制在第一个(服务器端)渲染


Ton*_*ony 2

我也尝试过并积累了经验:客户端cookie在客户端组件中可用,服务器cookie在服务器组件中可用。这些不同的 cookie 不能互相访问。看来这是 NextJS 的另一个缺陷