Next.js 14.1.4。在一个页面上选择一项服务,然后使用某些字段为此服务创建一个表单。对于表单,我使用 useFormik 和 Yup 验证。为了处理提交表单,我需要向服务器上的 CRM 系统发出请求并在那里创建潜在客户。我为此创建了一个服务器函数(使用 use server 指令)。在客户端的handleSubmit 中,我使用用户选择的参数调用此函数。服务器函数不带参数调用,没有参数一切都会发生。但任务恰恰是提出论点。我有一个类似的案例,但是可以说表格是提前呈现的。现在逻辑如下 - 显示服务选择块 - 当您单击服务时,在客户端上呈现一个表单。在提交此表单时,我无法使用参数调用服务器函数,它是不带参数调用的。
索引页
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.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'>Мы ценим ваше время и стремимся обеспечить быструю и эффективную обработку заявок. Пожалуйста, заполняйте все поля в форме максимально корректно и полно. Это позволит Вам избежать дополнительных уточнений и сократить время на обработку вашей заявки.</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'>Поля, помеченные {'"*"'}, являются обязательными к заполнению</p>
</div>
<Form inputs={service?.inputs} validationSchema={validationSchema} fields={fields} filesKey={filesKey}/>
</>
)
}
return (<ServiceChoice types={types}/>)
}
表单.jsx
/* 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)
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>
)
}
bx-requests.jsx
"use server"
// Lead creating
export async function createLead (values) {
console.log('in lead creating', values)
return true
// requests logic
}
我尝试拉出表单,以便通过创建测试页面将其呈现在服务器上 测试页
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}/>
)
}
测试表
/* 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>
)
}
还是不行
错误是我将 File 对象传递给服务器函数