You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js 14.1.4中Formik结合Server Actions提交表单时参数无法传入的问题求助

Next.js 14.1.4中Formik结合Server Actions提交表单时参数无法传入的问题求助

大家好,我最近在开发Next.js 14.1.4项目时遇到了一个棘手的问题:用户先选择一项服务,页面会根据所选服务动态渲染对应字段的表单,表单用Formik+Yup做验证,提交时需要调用带"use server"指令的Server Action函数向CRM系统创建线索,但这个Server Action始终接收不到我传入的表单参数——明明在客户端的提交函数里能正常打印出values,传到Server Action里就变成空的了。

我的代码结构如下:

首页(IndexPage)

这是一个服务器组件,负责获取服务类型数据并传递给Manager组件:

import Manager from "@/components/manager"
import Header from "@/components/header"
import { getTypes } from "@/utils/requests"


export default async function Home() {

  const types = await getTypes()

  return (
    <>
      <Header/>
      <main>
        <Manager types={types}/>
      </main>
    </>
  )
}

Manager组件(Manager.jsx)

客户端组件,根据用户是否选择服务,切换显示服务选择器或动态生成的表单:

'use client'
import { useContext } from 'react'
import DataContext from '@/components/context/data-context'
import ServiceChoice from '@/components/organisms/service-choice'
import Form from '@/components/form'
import InlineSVG from 'react-inlinesvg'
import createFieldObject from '@/utils/create-fields-object'
import createYupSchema from '@/utils/create-yup-schema'
import Link from 'next/link'


export default function Manager ({types}) {

  const { service, setService } = useContext(DataContext)

  if (service) {

    const validationSchema = createYupSchema(service?.inputs)
    const { fields, filesKey } = createFieldObject(service?.inputs)

    return (
      <>
      <div className="instruction-container flex flex-col">
        <div className="flex justify-end">
          <button type='button' className='flex flex-row items-center cursor-pointer inherit-a-font' onClick={() => {setService(null)}}>
            К выбору услуг
            <InlineSVG src='/icons/arrow-left.svg' width={35} height={25} strokeWidth={2}/>
          </button>
        </div>
        <p className='inherit-a-font'>Мы ценим ваше время и&nbsp;стремимся обеспечить быструю и&nbsp;эффективную обработку заявок. Пожалуйста, заполняйте все&nbsp;поля в&nbsp;форме максимально корректно и&nbsp;полно. Это позволит Вам&nbsp;избежать дополнительных уточнений и&nbsp;сократить время на&nbsp;обработку вашей заявки.</p>
        <p className='inherit-a-font'>Вы всегда можете задать вопрос по заполнению, связавшись с нами по номеру телефону: <Link href='tel:88005502707'>{"8-(800)-550-27-07"}</Link></p>
        <p style={{color: 'red'}} className='inherit-a-font'>Поля,&nbsp;помеченные&nbsp;{"\"*\""},&nbsp;являются обязательными к&nbsp;заполнению</p>
      </div>
      <Form inputs={service?.inputs} validationSchema={validationSchema} fields={fields} filesKey={filesKey}/>
      </>
    )
  }

  return (<ServiceChoice types={types}/>)
}

表单组件(Form.jsx)

客户端组件,用Formik处理表单逻辑,提交时调用submitFunction,再由它调用Server Action:

/* eslint-disable react-hooks/rules-of-hooks */
'use client'

import { useFormik } from 'formik'
import InputManager from '@/components/inputs/input-manager'
import { motion } from 'framer-motion'
import { getOriginalImageUrl } from '@/utils/get-image-url'
import Link from 'next/link'
import InlineSVG from 'react-inlinesvg'
import { useContext, useState } from 'react'
import DataContext from '@/components/context/data-context'
import CircleLoader from '@/components/atoms/circle-loader'
import { createLead } from '@/utils/bx-requests'


const submitFunction = async (values, filesKey, serviceName) => {
  // try {
    console.log(values, filesKey, serviceName) // 这里能正常打印出用户填写的values
    const status = await createLead(values)
    // console.log(status)
  // }
  // catch (error) {
  //   console.log(error)
  // }
}


export default function Form ({inputs, validationSchema, fields, filesKey}) {

  if (!Array.isArray(inputs)) return (<></>)
  
  const { service } = useContext(DataContext)

  const [isLoading, setIsLoading] = useState(false)
  const [isSuccess, setIsSuccess] = useState(false)

  const formik = useFormik({
    initialValues: fields,
    validationSchema: validationSchema,
    onSubmit: (values) => {submitFunction(values, filesKey, service.name)}
  })


  return (
    <form onSubmit={formik.handleSubmit} className='flex flex-col'>
      <div className={`inputs-container flex flex-col ${isLoading || isSuccess ? 'disabled' : ''}`}>
        {inputs.map((item) => (
          <div className="input-container flex flex-col" key={item.label}>
            <label className='flex flex-row items-center flex-wrap' style={{gap: '5px'}}>
              {item.label}{item.isRequired ? <span style={{fontSize: 'inherit', fontWeight: 'inherit', color: 'red'}}> * </span> : <></>}
              {item?.file?.data && <Link href={getOriginalImageUrl(item.file)} target='_blank' alt='Ссылка на файл' className='inherit-label-font'>{`(${item.filename || "Ссылка"})`}</Link>}
              {formik.errors[item.bitrixKey] && formik.touched[item.bitrixKey] && 
                <motion.span
                  initial={{opacity: 0}}
                  animate={{opacity: 1}}
                  className='inherit-p-font'
                  style={{ color: 'red'}}>
                    {formik.errors[item.bitrixKey].toLowerCase()}
                </motion.span>
              }
            </label>
            <InputManager input={item} formik={formik}/>
          </div>
        ))}
      </div>  
      <button className="flex flex-row items-center" style={{alignSelf: 'flex-end'}} disabled={isSuccess || isLoading} type='submit'>
        <p className='inherit-input-font'>{isSuccess ? "Успешно" : "Отправить заявку"}</p>
        {isLoading ? <CircleLoader/> : <InlineSVG src='/icons/upload.svg' width={30} height={20}/>}
      </button>
    </form>
  )

}

Server Action文件(bx-requests.jsx)

这里的createLead函数打印的values始终为空:

"use server"
// Lead creating
export async function createLead (values) {
  console.log('in lead creating', values) // 这里的values总是undefined或者空对象
  return true
  // requests logic

}

我尝试过的解决方案

我特意创建了一个测试页,提前在服务器端渲染表单(不需要用户选择服务动态切换),但问题依然存在,Server Action还是接收不到参数:

测试页(TESTPAGE)

import Form from "@/components/formTest";
import { getTypes } from "@/utils/requests";

export default async function Page () {

  const types = await getTypes()
  const service = types[2].attributes

  return (
    <Form inputs={service.inputs} service={service}/>
  )
}

测试表单(TESTFORM)

/* eslint-disable react-hooks/rules-of-hooks */
'use client'

import { useFormik } from 'formik'
import InputManager from '@/components/inputs/input-manager'
import { motion } from 'framer-motion'
import { getOriginalImageUrl } from '@/utils/get-image-url'
import Link from 'next/link'
import InlineSVG from 'react-inlinesvg'
import { useContext, useState } from 'react'
import DataContext from '@/components/context/data-context'
import CircleLoader from '@/components/atoms/circle-loader'
import { createLead } from '@/utils/bx-requests'
import createFieldObject from '@/utils/create-fields-object'
import createYupSchema from '@/utils/create-yup-schema'


const submitFunction = async (values, filesKey, serviceName) => {
  // try {
    console.log(values, filesKey, serviceName)
    const status = await createLead(values)
    // console.log(status)
  // }
  // catch (error) {
  //   console.log(error)
  // }
}


export default function Form ({inputs, service}) {

  const validationSchema = createYupSchema(service?.inputs)
  const { fields, filesKey } = createFieldObject(service?.inputs)


  if (!Array.isArray(inputs)) return (<></>)

  const [isLoading, setIsLoading] = useState(false)
  const [isSuccess, setIsSuccess] = useState(false)

  const formik = useFormik({
    initialValues: fields,
    validationSchema: validationSchema,
    onSubmit: (values) => {submitFunction(values, filesKey, service.name)}
  })


  return (
    <form onSubmit={formik.handleSubmit} className='flex flex-col'>
      <div className={`inputs-container flex flex-col ${isLoading || isSuccess ? 'disabled' : ''}`}>
        {inputs.map((item) => (
          <div className="input-container flex flex-col" key={item.label}>
            <label className='flex flex-row items-center flex-wrap' style={{gap: '5px'}}>
              {item.label}{item.isRequired ? <span style={{fontSize: 'inherit', fontWeight: 'inherit', color: 'red'}}> * </span> : <></>}
              {item?.file?.data && <Link href={getOriginalImageUrl(item.file)} target='_blank' alt='Ссылка на файл' className='inherit-label-font'>{`(${item.filename || "Ссылка"})`}</Link>}
              {formik.errors[item.bitrixKey] && formik.touched[item.bitrixKey] && 
                <motion.span
                  initial={{opacity: 0}}
                  animate={{opacity: 1}}
                  className='inherit-p-font'
                  style={{ color: 'red'}}>
                    {formik.errors[item.bitrixKey].toLowerCase()}
                </motion.span>
              }
            </label>
            <InputManager input={item} formik={formik}/>
          </div>
        ))}
      </div>  
      <button className="flex flex-row items-center" style={{alignSelf: 'flex-end'}} disabled={isSuccess || isLoading} type='submit'>
        <p className='inherit-input-font'>{isSuccess ? "Успешно" : "Отправить заявку"}</p>
        {isLoading ? <CircleLoader/> : <InlineSVG src='/icons/upload.svg' width={30} height={20}/>}
      </button>
    </form>
  )

}

现在我实在找不到问题出在哪了,有没有朋友遇到过类似的情况?或者我的代码里有什么地方写得不对?

备注:内容来源于stack exchange,提问作者user23915831

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 12:44:53