Next.js. Server actions in form using formik. Action with arguments didnt work

39 Views Asked by At

Next.js 14.1.4. There is a page on which a service is selected and then a form is created for this service with certain fields. For the form, I use useFormik and Yup validation. To process the submit form, I need to make requests to the CRM system on the server and create a lead there. I created a server function for this (with the use server directive). In handleSubmit on the client, I call this function with the arguments that the user has selected. The server function is not called with arguments, everything goes without them. But the task is precisely to throw arguments. I had a similar case, but the form was rendered there in advance, so to speak. Now the logic is as follows - the service selection block is displayed - when you click on the service, a form is rendered on the client. In the submit of this form, I cannot call the server function with arguments, it is called without arguments.

IndexPage

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'>Мы ценим ваше время и&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

/* 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

}

I tried to pull out the form so that it is rendered on the server by creating a testPage 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>
  )

}

still not working

1

There are 1 best solutions below

1
user23915831 On

the error was that I was passing File objects to the server function