I'm using Next.JS 13.4.19 with the new app
folder to enable React Server Components, and running into issues trying to use client sub-components (i.e. <ClientComponent.SubComponent \>
) inside a server component.
我将Next.JS 13.4.19与新的app文件夹一起使用,以启用Reaction服务器组件,并且在尝试使用服务器组件中的客户端子组件(即
)时遇到问题。
ClientTest.jsx
is a Client component with sub-component attached (pretend this has some state or other reason to be client component)
Jsx是一个附加了子组件的客户端组件(假定这是客户端组件的状态或其他原因)
'use client'
export default function ClientTest() {
return <div>ClientTest</div>
}
ClientTest.Item = function ClientTestItem() {
return <div>ClientTest.Item</div>
}
page.jsx
is a React Server Component (allows access to db or file system)
Jsx是一个Reaction服务器组件(允许访问数据库或文件系统)
import ClientTest from './ClientTest'
export default function Page() {
// ClientTest.Item is undefined, and the following line errors with:
// Unsupported Server Component type: undefined
return (
<ClientTest.Item />
);
}
it seems the main component <ClientTest />
works fine, but the attached <ClientTest.Item />
is undefined :(
主组件
似乎工作正常,但附加的
未定义:(
https://codesandbox.io/p/sandbox/currying-grass-wsvtn4?file=/app/page.tsx
Https://codesandbox.io/p/sandbox/currying-grass-wsvtn4?file=/app/page.tsx
Possible workaround would be to simply avoid sub-components in the server compoonent by making another wrapper client component (importing ClientTest.Item in a client component works fine) but I would greatly prefer if this could be fixed through some magic webpack configuration? or maybe it's a bug in next.js?
可能的解决方法是通过创建另一个包装器客户端组件(在客户端组件中导入ClientTest.Item可以很好地工作)来简单地避免服务器组件中的子组件,但我更希望通过一些神奇的webpack配置来解决这个问题?或者可能是next.js中的错误?
For the record; Importing server sub-components in server components works fine, as does importing client sub-components in client components.
需要指出的是,在服务器组件中导入服务器子组件很好,在客户端组件中导入客户端子组件也是如此。
Edit:
编辑:
I am working on a component library which uses the sub-component pattern quite a bit, so it makes a big difference to me if this is a "working as intended" part-of-the-design of server components, or a "current implementation limitation"/bug.
我正在开发一个组件库,它经常使用子组件模式,所以这是服务器组件设计的“按预期工作”部分,还是“当前实现限制”/错误,对我来说都有很大的不同。
If it is a permanent design limitation we might need to rethink our entire naming scheme and architecture to avoid terrible DX, but I haven't found anything conclusive about this in the react docs.
如果这是一个永久性的设计限制,我们可能需要重新考虑我们的整个命名方案和体系结构,以避免糟糕的DX,但我在Reaction文档中没有找到任何关于这一点的确凿证据。
更多回答
优秀答案推荐
You do everything right, this is the Next.js problem. I just wrote a broad explanation of this behaviour. Please see
https://stackoverflow.com/a/77077564/9047572
你做的每件事都是正确的,这就是Next.js的问题。我刚刚写了一篇关于这一行为的广泛解释。请参阅https://stackoverflow.com/a/77077564/9047572
TL:DR: you need that 'use client' directive to use client-only features. Once the component became client-side, its nested components are client-side too. No way to change or intercept this behavior.
TL:DR:您需要‘Use Client’指令才能使用仅限客户端的功能。一旦组件成为客户端,它的嵌套组件也是客户端。无法改变或拦截这种行为。
I've found a better workaround for my case (as library author who needs to publish client sub-components exposed to server components), and posting it here in case someone stumbles across the same issue;
我已经为我的案例找到了一个更好的解决方法(作为需要发布向服务器组件公开的客户端子组件的库作者),并将其发布在这里,以防有人遇到相同的问题;
Instead of attaching the sub-component inside the component file with use client
, I can attach and re-export in a separate file. Using an index.js
file I can keep the import path the same for consumers, and since this file does not have the 'use client' directive it will be parsed properly when imported in a React Server Component:
我可以在单独的文件中附加并重新导出,而不是使用Use Client附加组件文件中的子组件。使用index.js文件,我可以为消费者保持相同的导入路径,并且由于该文件没有‘Use Client’指令,因此在导入到Reaction服务器组件中时,它将被正确解析:
ClientTest/ClientTest.jsx
客户端测试/客户端测试.jsx
'use client'
export default function ClientTest() {
return <div>ClientTest</div>
}
ClientTest/ClientTest.Item.jsx
客户端测试/客户端测试.Item.jsx
'use client'
export default function ClientTestItem() {
return <div>ClientTest.Item</div>
}
ClientTest/index.js
(note no 'use client'
in this file)
ClientTest/index.js(注意此文件中没有‘使用客户端’)
import ClientTest from "./ClientTest";
import ClientTestItem from "./ClientTest.Item";
ClientTest.Item = ClientTestItem
export default ClientTest
page.jsx
is as before, but should now work fine
Jsx与以前一样,但现在应该可以正常工作
import ClientTest from './ClientTest' // now uses ClientTest/index.js
export default function Page() {
return (
<ClientTest.Item />
);
}
Updated codesandbox:
更新后的codesandbox:
https://codesandbox.io/p/sandbox/eloquent-hooks-x3qgl8?file=/app/ClientTest/index.ts
Https://codesandbox.io/p/sandbox/eloquent-hooks-x3qgl8?file=/app/ClientTest/index.ts
更多回答
Thank you for sharing! However it doesn't seem to be quite the same issue, I'm not nesting components, but rather attaching a component as a property of the exported component. Sub-component !== children, if that makes sense :)
谢谢分享!然而,这似乎不是一个完全相同的问题,我不是在嵌套组件,而是将组件附加为导出组件的属性。子组件!==子组件,如果有意义的话:)
我是一名优秀的程序员,十分优秀!