对 next.js 中服务器和客户端之间传递数据感到困惑

Cir*_*lus 5 javascript reactjs next.js next.js13

我无法理解 NextJS 13 中处理服务器端和客户端组件之间数据交换的正确方法。

我试图通过创建尽可能简单的场景来理解这一点。我有这两个函数可以简单地在服务器上读取和写入 JSON 文件:

import fs from 'fs'

export const saveData = async (data) => {
  const jsonData = JSON.stringify(data)
  try {
    await fs.promises.writeFile('data/data.json', jsonData)
  } catch (err) {
    console.log('Error', err)
  }
}

export const getData = async () => {
  try {
    const jsonData = await fs.promises.readFile('data/data.json')
    const data = JSON.parse(jsonData)
    return data

  } catch (err) {
    console.log('Error', err)
    return null
  }
}
Run Code Online (Sandbox Code Playgroud)

显示数据的 React 组件:

export const SomeComponent = ({ data }) => <p>{data.someKey}</p>
Run Code Online (Sandbox Code Playgroud)

以及一个获取用户输入的交互式 React 组件:

'use client'
import { useState } from 'react'

export default function Input({ handleInput }) {
  const [value, setValue] = useState()

  return (
    <div>
      <input type="text" onChange={(e) => setValue(e.target.value)}/>
      <button onClick={() => handleInput(value)}>Save</button>
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

如果我创建一个页面函数来获取数据并处理保存新数据,我无法将处理程序传递给输入组件,因为它是客户端组件。

import { SomeComponent } from './components/component'
import { getData, saveData } from './data'
import Input from './components/input'

export default async function Home() {
  const data = await getData()

  const handleInput = (data) => {
    saveData(data)
  }

  return (
    <div>
      <SomeComponent data={data} />
      {/* Error: */}
      <Input handleInput={handleInput}/>
    </div>
  )
}
Run Code Online (Sandbox Code Playgroud)

那么,在这个例子中,客户端和服务器之间的数据传递应该如何处理呢?

Fab*_*tis 4

谈到这个问题时,你基本上有两种选择:

  1. 选项 1:可以从客户端组件调用的 API,该组件更新文件并乐观地更新本地状态。可能是满足此类需求的更常见方法。

  2. 选项 2:与新挂钩结合传递到客户端组件的服务器操作useOptimistic。请记住,服务器操作仍处于 alpha 阶段,尚不建议在生产中使用。

我强烈建议您查看有关服务器操作的限制和规范的官方文档。这是一个简单的示例,如果实施,第二个选项可能会是什么样子:

您的服务器组件
const onSave = async (update: any) => {
  "use server";
  const { default: fs } = await import("fs"); 
  await fs.writeJSON("./myfile.json", update);
}

async function ServerComponent(): Promise<JSX.Element> {
  const data = await getMyData();
  return <ClientComponent data={data} onSave={onSave} />;
}
Run Code Online (Sandbox Code Playgroud)

use server指令用于将函数定义为服务器操作。请记住,服务器操作默认情况下不考虑会话,并且会为每个请求运行相同的函数。

您的客户端组件
function ClientComponent(props: Props): JSX.Element {
  const { data: initialData, onSave } = props;
  
  const [data, setData] = useState(initialData);

  const onUpdate = useCallback(async (update: any) => {
    setData(prev => ({ ...prev, ...update }));
    await onSave(update);
  }, [onSave]);

  return // ...
}
Run Code Online (Sandbox Code Playgroud)

由于当前的 alpha 状态,您必须在下一个配置文件中激活服务器操作,否则此示例将无法工作。