Home > OS >  is there a way to change form values on submit
is there a way to change form values on submit

Time:10-11

i have a situation where there are two buttons that i want acting as a submit.

how can i make the value of the zip code (inside formik) be combined with the value of the option (button onclick), e.g. {"option": "adult", "zip_code": "00000"}

i've found this: https://github.com/formium/formik/issues/467#issuecomment-369833667

but it's for a class component, and doesn't show how to get the button value into formik values...

import React, { useState, useEffect } from 'react';
import { Form, Formik, useField } from 'formik';
import * as Yup from 'yup';


const FORM_INPUT_BOX = "m-2 border border-gray-300 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:border-transparent"
const FORM_INPUT_LABEL = "ml-2 text-gray-700"
const FORM_INPUT_ERROR = "break-normal relative bottom-2 left-2 text-sm text-red-500"
const FORM_DROPDOWN = "m-2 focus:ring-yellow-500 border border-gray-300 focus:border-yellow-500 h-full border-transparent bg-transparent text-gray-500 sm:text-sm rounded"
export const FORM_BUTTON = "ml-2 mr-2 py-1 border border-transparent text-medium rounded-md text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"

const CustomTextInput = ({ label, ...props }) => {
    const [field, meta] =useField(props); //using the hook useField, and destructuring
    return(
        <> {/*so we don't return any div tags - its just a react fragment */}
        <label htmlFor={props.id || props.name} className={FORM_INPUT_LABEL}> {label} </label>
        <input className={FORM_INPUT_BOX}{...field}{...props} />
        {meta.touched && meta.error ? (
            <div className={FORM_INPUT_ERROR}>{meta.error}</div>
        ) : null}
        </>
    )
}

const ButtonMaker = (props) => {
    const {mainprops,userprops,adoptprops,...optionprops} = props
    return(
        <>
        <button className={FORM_BUTTON} id={optionprops.id} onClick={()=>{optionprops.setButtonClick(true);
                                                                          optionprops.setOption(optionprops.id)}}
        >{optionprops.label}</button>
        </>
    )
};

export default function RegistrationForm(props) {

    const {mainprops,userprops,...adoptprops} = props;
    const [buttonClick, setButtonClick] = useState(false);
    const [option, setOption] = useState('')
    const allprops = {
        mainprops,
        userprops,
        adoptprops,
        buttonClick,
        setButtonClick,
        setOption
    };

    useEffect(()=>{
        if (buttonClick){
        alert(option)};
        
    },[buttonClick])


    // https://regex101.com/ 
    const zipRegExp= /^([0-9]{5})$/;
    //zipRegExp: only 5 digits
    const zipRegExp_plus4= /^([0-9]{5})(?:[-\s]*([0-9]{4}))?$/;

    return (
        <Formik
            initialValues={{
                zip_code       :   ''
            }}
            validationSchema={Yup.object({
                zip_code: Yup.string()
                    .min(5,'five digits needed')
                    .matches(zipRegExp, 'invalid zipcode')
                    .required('required field'),
            })}
            onSubmit={ (values, { setSubmitting, resetForm }) => {
                setTimeout(() => {
                    // alert(JSON.stringify(values, null, 2));
                    resetForm();
                    setSubmitting(false);
            }, 1000)
            }}
        >
            {props => (
                <Form className='bg-white rounded px-8 py-8 mt-4 mb-4'>
                    <div className='flex justify-center align-center'>
                    <section className='m-2 w-52 flex flex-col border'>
                        <CustomTextInput label='Enter ZIP Code' name='zip_code' placeholder='zipcode' />
                    </section>
                    </div>
                    <br></br>
                    <div className='flex justify-center align-center'>
                    <ButtonMaker {...allprops} groupa='pet' groupb='adopt' id='adult' label='adult'/>
                    <ButtonMaker {...allprops} groupa='pet' groupb='adopt' id='puppy' label='puppy'/>
                    </div>
                </Form>
            )}
        </Formik>
    )
}

something else i tried:

added this to each button: formkprops={props}

and this to the button maker on click function: formkprops.submitForm()

but it doesn't work.

CodePudding user response:

Try the following:

import React, { useState, useEffect } from 'react';
import { Form, Formik, useField } from 'formik';
import * as Yup from 'yup';


const FORM_INPUT_BOX = "m-2 border border-gray-300 bg-white text-gray-700 placeholder-gray-400 shadow-sm text-base focus:outline-none focus:ring-2 focus:ring-yellow-500 focus:border-transparent"
const FORM_INPUT_LABEL = "ml-2 text-gray-700"
const FORM_INPUT_ERROR = "break-normal relative bottom-2 left-2 text-sm text-red-500"
const FORM_DROPDOWN = "m-2 focus:ring-yellow-500 border border-gray-300 focus:border-yellow-500 h-full border-transparent bg-transparent text-gray-500 sm:text-sm rounded"
export const FORM_BUTTON = "ml-2 mr-2 py-1 border border-transparent text-medium rounded-md text-white bg-gray-600 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500"

const CustomTextInput = ({ label, ...props }) => {
    const [field, meta] =useField(props); //using the hook useField, and destructuring
    return(
        <> {/*so we don't return any div tags - its just a react fragment */}
        <label htmlFor={props.id || props.name} className={FORM_INPUT_LABEL}> {label} </label>
        <input className={FORM_INPUT_BOX}{...field}{...props} />
        {meta.touched && meta.error ? (
            <div className={FORM_INPUT_ERROR}>{meta.error}</div>
        ) : null}
        </>
    )
}

const ButtonMaker = (props) => {
    const {mainprops,userprops,adoptprops,...optionprops} = props
    return(
        <>
        <button className={FORM_BUTTON} id={optionprops.id} onClick={()=>{optionprops.setButtonClick(true);
                                                                          optionprops.setOption({"option": optionprops.id, "zipcode": optionprops.zip_code})}}
        >{optionprops.label}</button>
        </>
    )
};

export default function RegistrationForm(props) {

    const {mainprops,userprops,...adoptprops} = props;
    const [buttonClick, setButtonClick] = useState(false);
    const [option, setOption] = useState(null)
    const allprops = {
        mainprops,
        userprops,
        adoptprops,
        buttonClick,
        setButtonClick,
        setOption
    };

    useEffect(()=>{
        if (buttonClick){
        alert(JSON.stringify(option, null, 4))};
        
    },[buttonClick])


    // https://regex101.com/ 
    const zipRegExp= /^([0-9]{5})$/;
    //zipRegExp: only 5 digits
    const zipRegExp_plus4= /^([0-9]{5})(?:[-\s]*([0-9]{4}))?$/;

    return (
        <Formik
            initialValues={{
                zip_code       :   ''
            }}
            validationSchema={Yup.object({
                zip_code: Yup.string()
                    .min(5,'five digits needed')
                    .matches(zipRegExp, 'invalid zipcode')
                    .required('required field'),
            })}
            onSubmit={ (values, { setSubmitting, resetForm }) => {
                setTimeout(() => {
                    // alert(JSON.stringify(values, null, 2));
                    resetForm();
                    setSubmitting(false);
            }, 1000)
            }}
        >
            {props => (
                <Form className='bg-white rounded px-8 py-8 mt-4 mb-4'>
                    <div className='flex justify-center align-center'>
                    <section className='m-2 w-52 flex flex-col border'>
                        <CustomTextInput label='Enter ZIP Code' name='zip_code' placeholder='zipcode' />
                    </section>
                    </div>
                    <br></br>
                    <div className='flex justify-center align-center'>
                    <ButtonMaker {...allprops} groupa='pet' groupb='adopt' id='adult' label='adult' zip_code={props.values.zip_code}/>
                    <ButtonMaker {...allprops} groupa='pet' groupb='adopt' id='puppy' label='puppy' zip_code={props.values.zip_code}/>
                    </div>
                </Form>
            )}
        </Formik>
    )
}

You needed to create an option property and add a zip_code propery when the actionprops' setoption function is called. In order to display this in the alert, you needed to use the JSON.stringify function to display the option using the alert function.

**Solution for Information Requested In Comments Section by Original Poster **

You can create state management for your application or use a library for this like Redux. Inside the state management, you could have an array of objects similar to the one created above. You can then give whichever component you want access to this array. You would also need a final submit for the user to indicate when they are done, or a required amount of options the user has to choose before they can move on.

References:

StackOverflow. how to alert javascript object. https://stackoverflow.com/a/64941710/8121551. (Accessed 10 October, 2021).

  • Related