likes
comments
collection
share

Next.js客户端组件与服务端组件通讯示例

作者站长头像
站长
· 阅读数 59

问题背景

最近在边写边学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
评论
请登录