Next.js客户端组件与服务端组件通讯示例
问题背景
最近在边写边学Next.js,遇见了以下场景:
在一个页面中有两个同级的组件<Search />
(客户端组件搜索框)和<List />
(服务端组件列表),我想要基于<Search />
中的内容更新<List />
。但是该如何将<Search />
的value
传给<List/>
呢?
组件的简略代码如下:
// ./page.jsx 默认为服务端组件
export default function Test() {
return (
<div>
<Search></Search>
<List></List>
</div>
)
}
// ./search.jsx
'use client' // 只要使用了hooks,就必须声明为客户端组件
export default function (props) {
const [value, setValue] = useState('') // 需要传递的value
return (
<div>
<input type="text" onChange={(e) => {setValue(e.target.value)}}/>
<button>确认</button>
</div>
)
}
// ./list.jsx 默认为服务端组件
export default async function (props) {
// 基于<Search />的value发起请求
const res = await getData(props)
// 处理请求结果...
return (
<ul>
{res.data.map(...)}
</ul>
)
}
解决方法
经过本人的各种试验和踩坑,发现Next.js中客户端组件和服务端组件基本无法直接通讯。
- 使用redux/zustand❌
- 使用createContext/useContext❌
- 使用useRef❌
最后,终于发现可以通过Next.js提供的revalidatePath
、<Link />
或者useRouter
来进行间接的通讯。
首先,假设当前页面的路径为http://localhost/test
。
重写<Search />
组件如下:
// ./search.jsx
'use client' // 只要使用了hooks,就必须声明为客户端组件
// +导入next路由+
import Link from 'next/link'
export default function (props) {
const [value, setValue] = useState('') // 需要传递的value
return (
<div>
<input type="text" onChange={(e) => {setValue(e.target.value)}}/>
{/*+点击按钮,将value以路由参数的形式传给当前页面+*/}
<Link href=`/test?value=${value}`>
<button>确认</button>
</Link>
</div>
)
}
为了获取<Search />
传递的参数,重写page.tsx
如下:
// ./page.jsx 默认为服务端组件
export default function Test({ params, searchParams}) {
// +路由参数保存于页面props的searchParams字段中+
const value = searchParams.value
return (
<div>
<Search></Search>
{/*+再将value传给服务端组件+*/}
<List value={value}></List>
</div>
)
}
此时,<List />
终于可以根据<Search />
的value
动态渲染了。
总结
也许,你会问为什么要大费周章的进行客户端组件和服务端组件的通讯呢,直接把<List />
也改为客户端组件不就行了?
这确实是一种更直接的方法。但是,服务端组件的优势在于:
最后,修改后的整体示例如下:
// ./page.jsx 默认为服务端组件
export default function Test({ params, searchParams}) {
// +路由参数保存于页面props的searchParams字段中+
const value = searchParams.value
return (
<div>
<Search></Search>
{/*+再将value传给服务端组件+*/}
<List value={value}></List>
</div>
)
}
// ./search.jsx
'use client' // 只要使用了hooks,就必须声明为客户端组件
import Link from 'next/link'
export default function (props) {
const [value, setValue] = useState('') // 需要传递的value
return (
<div>
<input type="text" onChange={(e) => {setValue(e.target.value)}}/>
{/*+将value以路由参数的形式传给当前页面+*/}
<Link href=`/test?value=${value}`>
<button>确认</button>
</Link>
</div>
)
}
// ./list.jsx 默认为服务端组件
export default async function ({value}) {
// 基于<Search />的value发起请求
const res = await getData(value)
// 处理请求结果...
return (
<ul>
{res.data.map(...)}
</ul>
)
}
转载自:https://juejin.cn/post/7361639833864454198