reactjs Typescript不接受来自useFormState上的服务器操作函数的附加属性

xxslljrj  于 5个月前  发布在  React
关注(0)|答案(2)|浏览(77)

我在尝试将Next.js服务器操作与useFormState(用于在客户端显示输入错误)和Typescript集成时遇到了麻烦。
根据他们的官方文档here,他们建议为服务器操作函数添加一个新的prop,例如:

export async function createInvoice(prevState: State, formData: FormData)

字符串
在他们的示例中,他们将服务器动作函数作为第一个参数添加到useFormState中,如下所示:

const [state, dispatch] = useFormState(createInvoice, initialState);


在我的情况下,这是:

const [state, dispatch] = useFormState(action, initialState);


其中action是从表单页面接收的服务器操作函数。
Typescript抱怨action类型,如何修复它?

No overload matches this call.
  Overload 1 of 2, '(action: (state: { message: null; errors: {}; }) => Promise<{ message: null; errors: {}; }>, initialState: { message: null; errors: {}; }, permalink?: string | undefined): [state: { message: null; errors: {}; }, dispatch: () => void]', gave the following error.
    Argument of type '(prevState: State, formData: FormData) => void' is not assignable to parameter of type '(state: { message: null; errors: {}; }) => Promise<{ message: null; errors: {}; }>'.
      Target signature provides too few arguments. Expected 2 or more, but got 1.
  Overload 2 of 2, '(action: (state: { message: null; errors: {}; }, payload: FormData) => Promise<{ message: null; errors: {}; }>, initialState: { message: null; errors: {}; }, permalink?: string | undefined): [state: ...]', gave the following error.
    Argument of type '(prevState: State, formData: FormData) => void' is not assignable to parameter of type '(state: { message: null; errors: {}; }, payload: FormData) => Promise<{ message: null; errors: {}; }>'.
      Type 'void' is not assignable to type 'Promise<{ message: null; errors: {}; }>'.ts(2769)
(parameter) action: (prevState: State, formData: FormData) => void


遵循我的表单组件代码:

import { useFormState } from "react-dom";
import { State } from "@/types/formState";

type Props = {
  children: React.ReactNode;
  action: string | ((prevState: State, formData: FormData) => void) | undefined;
};

const Form = ({ children, action }: Props) => {
  const initialState = { message: null, errors: {} };

  const [state, dispatch] = useFormState(action, initialState);

  return (
    <form
      action={dispatch}
      className="w-full flex justify-center"
      autoComplete="off"
    >
      <div className={`w-full`}>
        {children}
      </div>
    </form>
  );
};

export default Form;


调用上面Form组件的页面:

import { createUserAccount } from "@/actions/createUserAccount";
import Form, { Button, InputText } from "@/components/Form";

type Props = {};

const SignUpPage = (props: Props) => {
  return (
    <Form action={createUserAccount}>
      <div className="items-center mb-4 flex relative">
        <InputText
          name="firstname"
          type="text"
          placeholder="Enter your first name"
          required
        />
      </div>

      <div className="items-center mb-4 flex relative">
        <InputText
          name="lastname"
          type="text"
          placeholder="Enter your last name"
          required
        />
      </div>

      <div className="items-center mb-4 flex relative">
        <Button title="Join Now" type="submit" />
      </div>
    </Form>
  );
};

export default SignUpPage;


我的服务器操作函数(createUserAccount):

"use server";

import { State } from "@/types/formState";
import userAccountSchema from "@/validation/schemas/createUserAccount";

export async function createUserAccount(prevState: State, formData: FormData) {
  const parsedData = userAccountSchema.safeParse({
    firstname: formData.get("firstname"),
    lastname: formData.get("lastname"),
  });

  // Check if the parsing was not successful
  if (!parsedData.success) {
    return {
      errors: parsedData.error.flatten().fieldErrors,
    };
  }

  // Process validated data
  // ...
  return { success: true };
}


代码返回的输入错误在测试时没有任何问题。问题显然只是关于 typescript 。
谢谢你,谢谢

编辑#1

关注我的包。json:

{
  "name": "project_name",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "next": "14.0.4",
    "react": "^18.2.46",
    "react-dom": "^18.2.18",
    "sass": "^1.69.7",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/node": "^20.10.6",
    "@types/react": "^18.2.46",
    "@types/react-dom": "^18.2.18",
    "autoprefixer": "^10.0.1",
    "eslint": "^8.56.0",
    "eslint-config-next": "14.0.4",
    "postcss": "^8.4.33",
    "tailwindcss": "^3.4.0",
    "typescript": "^5.3.3"
  }
}

r7knjye2

r7knjye21#

action prop 的正确签名应该是:

type Props = {
  action: (prevState: State, formData: FormData) => State | Promise<State>;
  /* ... */
};

字符串
它必须是一个函数,该函数必须返回一个新的State或一个新State的Promise。
它不可能是string
您可能认为它是一个字符串,因为您正在将回调传递给<form action="...">
在vanilla React中,你必须使用一个字符串作为表单操作,但在NextJS中,你可以传递函数。
并且action是一个必需的参数,所以它不能是undefined

zfycwa2u

zfycwa2u2#

在使用useFormState的服务器操作中,操作的第一个参数的类型和返回值的类型必须相同:

action: (prevState: State, formData: FormData) => void;

字符串
prevState的类型为State,因此返回值也应该相同

action: (prevState: State, formData: FormData) => Promise<State>;


这是因为useFormState类型是如何定义的:

function useFormState<State>(action: (state: Awaited<State>) => State | Promise<State>, initialState: Awaited<State>, permalink?: string): [state: Awaited<State>, dispatch: () => void]

相关问题