根据this RFC:

客户端组件可能not导入服务器组件或呼叫服务器 钩子/实用程序,因为它们只在服务器上工作.


我正在从事一个项目使用react 18.2和nextjs v14与应用程序路由.

我有一个我正在处理的页面:

enter image description here

页面是一个客户端组件,因为页面中的几乎所有内容都依赖于客户端功能. 这是一个表单,所以一切都取决于React useState()钩子.

Page.tsx的实际代码如下所示,它是一个很大的组件,我使用的是一个名为JoyUI的组件库(由MUI提供).

  • 重要的部分是我在代码中使用"use client"指令的第一行,因为正如我所提到的,该组件(NewModeratorPage)依赖于许多客户端功能.

  • 要查看的第二部分是<NewModeratorPage />组件的Return块中的<SelectCompany />组件,如下所示:

'use client'

import React from 'react'
import { Grid } from '@mui/material'
import useBreakpoints from '@/hooks/useBreakpoints'
import PasswordInput from '@/components/PasswordInput'
import AvatarUploader from '@/components/AvatarUploader'
import { Stack, Input, FormLabel, Checkbox } from '@mui/joy'
import {
  EmailRounded as EmailIcon,
  PhoneRounded as PhoneIcon,
  PersonRounded as PersonIcon,
} from '@mui/icons-material'
import SelectCompany from './SelectCompany'

const NewModeratorPage = () => {
  const { isDownMd } = useBreakpoints()

  return (
    <Grid container spacing={2}>
      <Grid item xs={12} md={3}>
        <AvatarUploader placeholder={<PersonIcon sx={{ fontSize: 64 }} />} />
      </Grid>
      <Grid item xs={12} md={8}>
        <Stack spacing={1}>
          <FormLabel>Name</FormLabel>
          <Stack direction={isDownMd ? 'column' : 'row'} spacing={2}>
            <Input placeholder="First name" size="sm" fullWidth required />
            <Input placeholder="Last name" size="sm" fullWidth required />
          </Stack>
          <FormLabel>Credentials</FormLabel>
          <Stack direction={isDownMd ? 'column' : 'row'} spacing={2}>
            <Input
              placeholder="Email"
              startDecorator={<EmailIcon />}
              type="email"
              size="sm"
              fullWidth
              required
            />
            <Input placeholder="Phone number" startDecorator={<PhoneIcon />} size="sm" fullWidth />
          </Stack>
          <Stack direction={isDownMd ? 'column' : 'row'} spacing={2}>
            <PasswordInput size="sm" fullWidth required />
          </Stack>
          <Checkbox size="sm" label="Send password via email" defaultChecked />
          <SelectCompany />
        </Stack>
      </Grid>
    </Grid>
  )
}

export default NewModeratorPage

  • <SelectCompany />等于server component. 它呈现一个<Select>,其中<Options>是从API获取的.

因此,<SelectCompany />涉及数据获取.

这是一个很好的指示符,表明该组件应该是服务器组件.

  • 一旦用户请求页面,页面将与表单一起提供,并预取<SelectCompany/>个包含所有数据的页面.

因此,没有加载时间来获取客户端的公司.

  • 它使用"use server"指令,以表明这是一个服务器组件.

下面是<SelectCompany />服务器组件的代码.

"use server"

import React, { PropsWithChildren } from 'react'
import { Select, Option, Avatar, Stack } from '@mui/joy'

export interface SelectCompanyProps extends PropsWithChildren {}

const SelectCompany = async ({ children, ...props }: SelectCompanyProps) => {
  const response = await fetch('/api/companies')
  const data = await response.json()
  console.log(data)

  return (
    <Select value={1}>
      <Option value={1}>
        <Stack direction="row" spacing={2}>
          <Avatar size="sm" />
          <React.Fragment>Company 1</React.Fragment>
        </Stack>
      </Option>
    </Select>
  )
}

export default SelectCompany

首先,我在浏览器中收到以下错误:

Error: async/await is not yet supported in Client Components, only Server Components. This error is often caused by accidentally adding `'use client'` to a module that was originally written for the server.

在终端(我使用的是Linux btw)中,我收到以下错误:

Internal error: Error: Server Functions cannot be called during initial render. This would create a fetch waterfall. Try to use a Server Component to pass data to Client Components instead.

据我所知,这个错误的原因是不允许客户端组件导入服务器组件,

那么,我的案件的解决方案是什么?我希望<SelectCompanies />作为服务器组件.

推荐答案

nextjs docs开始,有几种支持和不支持的模式

Unsupported

'use client'
 
// You cannot import a Server Component into a Client Component.
import ServerComponent from './Server-Component'
 
export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
 
      <ServerComponent />
    </>
  )
}

这就是您try 的方法,您可以将服务器组件作为子组件传递,如下所示:

Supported pattern

'use client'
 
import { useState } from 'react'
 
export default function ClientComponent({
  children,
}: {
  children: React.ReactNode
}) {
  const [count, setCount] = useState(0)
 
  return (
    <>
      <button onClick={() => setCount(count + 1)}>{count}</button>
      {children}
    </>
  )
}
// This pattern works:
// You can pass a Server Component as a child or prop of a
// Client Component.
import ClientComponent from './client-component'
import ServerComponent from './server-component'
 
// Pages in Next.js are Server Components by default
export default function Page() {
  return (
    <ClientComponent>
      <ServerComponent />
    </ClientComponent>
  )
}

通过将它们作为子级传递,我们知道所有服务器组件,并可以呈现/流传输它们.

Reactjs相关问答推荐

当我在React中使用类方法更新模型的状态时,它在严格模式下触发两次

try 在Dockerfile中使用AWS CLI将React构建工件上传到S3时出错

以下代码是否存在安全问题?

ReactJs;Reaction-Hook-Form:仅当 Select 了特定 Select 列表选项时才显示文本输入

react -在选项中 Select 我想要显示和图像.但当我 Select 该选项时,我希望控件仅显示该选项的文本部分

是否在Extra Reducer完成作业(job)时触发createListenerMiddleware?

捕获表单数据时的Reactjs问题

Reaction-Query&-使用Mutation触发成功,而应该触发错误

同一文件中前端和后端的Nginx配置

Reaction-路由-DOM v6与v5

如何使用 React 获取表单中的所有值?

错误:无法获取 RSC 负载.返回浏览器导航

Chakra UI:如何在选中状态下设置复选框 colored颜色

在 monorepo 内的工作区或库之间共享 redux 状态

如何让 useEffect 在 React.JS 中只运行一次?

在 reactJS 中导航时页面重新加载

如何测试使用 React.createportal 呈现的组件?

React js中不记名令牌中的空间问题

为什么我从另一个组件收到错误?

bootstrap 导航栏切换器在 reactjs 中不起作用