Next.js 中关于'use client'的使用误解及解法

client component vs server component

我们在 page.tsx中导入 Button 自定义组件

tsx 复制代码
// app/page.tsx
import styles from './page.module.css'
import Button from './components/button'
export default function Home() {
	return (
		<main className={styles.main}>
			<h1>hello world</h1>
			<Button />
		</main>
	)
}

由于 nextjs 默认 src之下的组件都是 server component,在Button组件(server component)中,如果直接调用client component 才能使用的交互性例如 onClick useState是会报错的

可以简单按照报错提示修改为 client component 这样就没问题

但是如果你稍不注意直接在 page.tsx 加上'use client'也是没有报错的!

但是这样造成的问题是:导入page.tsx 的所有组件都将是'client component',那和直接写 react 有什么区别..

tsx 复制代码
'use client'
import styles from './page.module.css'
import Button from './components/button'
import Post from './components/post'

export default function Home() {
	return (
		<main className={styles.main}>
			<h1>hello world</h1>
			<Button />
            <Post/>
		</main>
	)
}

假设 Post 组件中需要加载一个很大的第三方库例如sanitize-html

如果放在client component 就不太适宜,可以看官方文档的第四条,将导入的较大第三方库尽量放在server component上减少客户端的压力

所以最优的解决办法就是:谁需要交互性就将最叶子节点变成客户端组件

✔️

使用 context 封装 children

假设你需要使用一个上下文 context 传递一些数据,你可以这样写

tsx 复制代码
// app/context/ThemeComtext.tsx
'use client'
export default function ThemeContextProvider({ children }: { children: React.ReactNode }) {
	return <div>{children}</div>
}

使用时在 layout 上封装一层 wrapper

tsx 复制代码
import type { Metadata } from 'next'
import { Inter } from 'next/font/google'
import './globals.css'
import ThemeContextProvider from './context/ThemeContext'

const inter = Inter({ subsets: ['latin'] })
export default function RootLayout({ children }: { children: React.ReactNode }) {
	return (
		<html lang="en">
			<body className={inter.className}>
				<ThemeContextProvider>{children}</ThemeContextProvider>
			</body>
		</html>
	)
}

ThemeContextProvider其实是一个客户端组件,如果把 children 根组件 wrapper 在 这个context 中根据上面的说法岂不是之下的所有组件也都是客户端组件了?

正确思考方式是 : 关注import tree 而不是 render tree

上一个例子中,我们在一个使用'use client'的客户端组件中 import其他组件,那么这些组件都会变成客户端组件,而这里的例子其实只是传递组件children,并不会将 children 中的服务端组件改成客户端组件

总结就是 改变组件和 jsx 渲染的结构无关,而是和import 相关联

多次导入交互性组件

将一个交互性的组件分别导入至 client && server 组件会是什么情况?

交互性组件Button

jsx 复制代码
import style from './button.module.css'
export default function Button() {
	return (
		<div className={style.btn} onClick={() => console.log('click me')}>
			Click me
		</div>
	)
}

server Component

jsx 复制代码
import sanitizeHtml from 'sanitize-html'
import Button from './button'
export default function Post() {
	return (
		<div>
			Post 
			<Button />
		</div>
	)
}

Client component

jsx 复制代码
'use client'
import Button from './button'
export default function Form() {
	return (
		<div>
			form <Button />
		</div>
	)
}

结果是符合直觉的 如果将 Post 组件删掉是不会报错的,也就是说在 client component使用Button会表现为client,而在 server component不会被影响,各自独立

参考

相关推荐
清沫几秒前
键盘效率提升指南(VSCode+Vim+SurfingKeys)
前端·vim·visual studio code
伍哥的传说1 小时前
鸿蒙系统(HarmonyOS)应用开发之手势锁屏密码锁(PatternLock)
前端·华为·前端框架·harmonyos·鸿蒙
yugi9878382 小时前
前端跨域问题解决Access to XMLHttpRequest at xxx from has been blocked by CORS policy
前端
浪裡遊2 小时前
Sass详解:功能特性、常用方法与最佳实践
开发语言·前端·javascript·css·vue.js·rust·sass
旧曲重听13 小时前
最快实现的前端灰度方案
前端·程序人生·状态模式
默默coding的程序猿3 小时前
3.前端和后端参数不一致,后端接不到数据的解决方案
java·前端·spring·ssm·springboot·idea·springcloud
夏梦春蝉3 小时前
ES6从入门到精通:常用知识点
前端·javascript·es6
马特说3 小时前
React金融数据分析应用性能优化实战:借助AI辅助解决18万数据量栈溢出Bug
react.js·金融·数据分析
归于尽3 小时前
useEffect玩转React Hooks生命周期
前端·react.js
G等你下课3 小时前
React useEffect 详解与运用
前端·react.js