How to implement radio buttons in ReactJS with Formik - reactjs

In my register form my radio buttons don't work, when I press the create button nothing happens, it doesn't activate the onSubmit function, but if I remove them it gets activated.
Here is my code:
import React from "react";
import { Formik, Field, Form, ErrorMessage } from "formik";
import * as Yup from "yup";
const male = props => (
<input type="radio" value="male" name="gender" {...props} />
);
const female = props => (
<input type="radio" value="female" name="gender" {...props} />
);
export class RegisterPage extends React.Component {
render() {
let { initialValues } = this.state;
return (
<div>
<div>
<h1>Sign Up</h1>
<Formik
initialValues={initialValues}
onSubmit={(
{ email, password, gender },
{ setStatus, setSubmitting }
) => {
setStatus();
authenticationservice.newuser({ email, password, gender }).then(
user => {
const { from } = this.props.location.state || { from: { pathname: "/" } `};`
this.props.history.push(from);
},
error => {
setSubmitting(false);
setStatus(error);
}
);
}}
render={({ errors, status, touched, isSubmitting }) => (
<Form>
<div>
<label htmlFor="email">Email Address</label>
<Field name="email" type="text" />
<ErrorMessage name="email" component="div" />
</div>
<div>
<label htmlFor="gender">Gender</label>
<label>Male</label>
<Field name="gender" as={male} />
<label>Female</label>
<Field name="gender" as={female} />
</div>
<div>
<button type="submit" disabled={isSubmitting}>
Create
</button>
{isSubmitting && <img alt="" src="data:image" />}
</div>
{status && <div>{status}</div>}
</Form>
)}
/>
</div>
</div>
);
}
}
I'm not sure what happens because I don't get any error codes, there is just not happening anything

Related

Warning: "unstable_flushDiscreteUpdates" on formik custom input

I am trying to create a custom input, so I can use it in my formik form, and I get this warning.
For the custom input I used this example: https://formik.org/docs/api/useField
P.S before adding this custom input, there wasn't any error.
My code:
const Input = (props) => {
const [meta] = useField(props);
return (
<div className={classes.control}>
<label htmlFor={props.id || props.name}>{props.label}</label>
<Field name={props.name} {...props} />
{meta.touched && meta.error ? <div className="error">{meta.error}</div> : null}
</div>
);
};
export default Input;
const RegistrationForm = () => {
const { isLoading, t, submitHandlerRegister, registrationValues } = useAuth();
return (
<Formik
initialValues={registrationValues}
validationSchema={RegisterValidationSchema}
onSubmit={submitHandlerRegister}
enableReinitialize={true}
>
<Form>
<Input
label={t("Authentication.Email")}
name="email"
type="email"
placeholder={t("Authentication.Email")}
></Input>
<Input
label={t("Authentication.Password")}
name="password"
type="password"
placeholder={t("Authentication.Password")}
></Input>
<div className={classes.actions}>
<button type="submit" disabled={isLoading}>
{t("Authentication.CreateAccount")}
</button>
</div>
</Form>
</Formik>
);
};

React Get User Info.(Input Values) On a Button Click

I've made a simple resume portal. What I want is to get all the inputs values displayed on a screen on submit. This all should be done when button(Generate CV) is clicked.
Here's my code below:
Child component ( src -> Routes -> UserForm -> Components -> UserDetails -> index.js )
import React from 'react'
import './style.scss';
import { Row, Col } from 'react-bootstrap'
const UserDetails = (props) => {
const { wrkExpTxtArea, AcQuTxtArea, clickToAdd, clickToRmv, addAcQuTxtArea, rmvAcQuTxtArea, inputChangeHandler } = props
return (
<>
<div className='UserDetails'>
<Row>
<Col lg='6'>
<div className='persnlInfo'>
<h4>
Personal Information
</h4>
<p>Your Name</p>
<input onChange={() => inputChangeHandler('name')} type="text" placeholder="Enter here" />
<p>Your Contact</p>
<input type="text" placeholder="Enter here" />
<p>Your Address</p>
<textarea className='formAddress' rows="5" cols="10" placeholder="Enter here" />
<p id='impLinks'>Important Links</p>
<p>Facebook</p>
<input type="text" placeholder="Enter here" />
<p>Instagram</p>
<input type="text" placeholder="Enter here" />
<p>Linkedin</p>
<input type="text" placeholder="Enter here" />
</div>
</Col>
<Col lg='6'>
<h4>
Professional Information
</h4>
<p>Objective</p>
<textarea className='formObjective' rows="5" cols="10" placeholder="Enter here" />
<p>Work Experience</p>
{wrkExpTxtArea.map(item => (
<textarea className='formWrkExp' value={item.value} rows="3" cols="10" placeholder="Enter here" />
))}
<div className='Button' >
<input type='button' value='Add' onClick={clickToAdd} />
<input type='button' value='Remove' onClick={clickToRmv} />
</div>
<p id='AcQu'>Academic Qualification</p>
{AcQuTxtArea.map(item => (
<textarea className='formAcQu' value={item.value} rows="3" cols="10" placeholder="Enter here" />
))}
<div className='Button' >
<input type='button' value='Add' onClick={addAcQuTxtArea} />
<input type='button' value='Remove' onClick={rmvAcQuTxtArea} />
</div>
</Col>
<Row>
<div className='sbmtButton'>
<input type='button' value='Generate CV' />
</div>
</Row>
</Row>
</div>
</>
)
}
export default UserDetails;
Parent component ( src -> Routes -> UserForm -> index.js )
import React from "react";
import Pages from "../../Components/HOC/Page/index";
import UserDetails from "../UserForm/Components/UserDetails/index";
class UserForm extends React.Component {
state = {
wrkExpTxtArea: [{ text: "" }],
AcQuTxtArea: [{ text: "" }],
inputValues: [{name: 'name', value: ''}],
};
inputChangeHandler = (e,inputName) => {
let updatedInputs = [...this.state.inputValues]
let changedInputValuesIndex = updatedInputs.findIndex(input => input.name === inputName)
if (changedInputValuesIndex > -1) {
let updatedInputValue =
{...updatedInputs[changedInputValuesIndex]}
updatedInputValue.value = e.target.value
updatedInputs[changedInputValuesIndex] = updatedInputValue
}
this.setState({inputValues: updatedInputs})
}
addTextArea = () => {
let updatedTextArea = [...this.state.wrkExpTxtArea];
updatedTextArea.push({ text: "" });
this.setState({ wrkExpTxtArea: updatedTextArea });
};
rmvTextArea = () => {
let updatedTextArea = [...this.state.wrkExpTxtArea];
if (updatedTextArea.length > 1) {
updatedTextArea.pop({ text: "" });
}
this.setState({ wrkExpTxtArea: updatedTextArea });
};
addAcQuTextArea = () => {
let updatedTextArea = [...this.state.AcQuTxtArea];
updatedTextArea.push({ text: "" });
this.setState({ AcQuTxtArea: updatedTextArea });
};
rmvAcQuTextArea = () => {
let updatedTextArea = [...this.state.AcQuTxtArea];
if (updatedTextArea.length > 1) {
updatedTextArea.pop({ text: "" });
}
this.setState({ AcQuTxtArea: updatedTextArea });
};
render() {
return (
<>
<Pages showHeader showFooter>
<UserDetails inputChangeHandler={this.inputChangeHandler} wrkExpTxtArea={this.state.wrkExpTxtArea} clickToAdd={this.addTextArea} clickToRmv={this.rmvTextArea}
AcQuTxtArea={this.state.AcQuTxtArea} addAcQuTxtArea={this.addAcQuTextArea} rmvAcQuTxtArea={this.rmvAcQuTextArea} />
</Pages>
</>
);
}
}
export default UserForm;
Output:
I'm new to programming and getting values of user inputs seems insanely complicated to me. I'm little aware that this can be achieved using state , props etc. But I really have no idea about Where and What code is to place. I need help. That’s it!
You can use useRef hook and give a reference to each of input element.
For Example
const name = useRef();
const handleSubmit = () => {
if(name.current && name.current.value){
console.log(name.current.value) // input element's value
}
}
return (<div>
<input type="text" ref={name} />
<button onClick={handleSubmit}> Submit </button>
</div>)
add an onChange prop to the input tag like this:
const [inputValue, setInputValue] = useState('')
const inputChangeHandler = (e) => {
// e argument has received by default from onChange
const newValue = e.target.value
setInputValue(newValue)
}
<input onChange={inputChangeHandler} />
whenever you start changing the value of the input, inputChangeHandler function will trigger and then update your state
index.js
import React, { useState } from "react";
import Pages from "../../Components/HOC/Page/index";
import UserDetails from "../UserForm/Components/UserDetails/index";
const initialData = {
name: '',
contact: '',
address: '',
facebook: '',
instagram: '',
linkedin: '',
objective: '',
workExperience: [],
academicQualification: [],
}
const UserForm = () => {
// holds all the form data from child component "UserDetails"
const [formData, setFormData] = useState(initialData)
const handleSubmit = () => {
// submit handler
alert(JSON.stringify(formData, undefined, 4))
}
return (
<>
<Pages showHeader showFooter>
<UserDetails form={formData} setter={setFormData} onSubmit={handleSubmit} />
</Pages>
</>
)
}
export default UserForm;
UserDetails
import React, { useState } from 'react'
import './style.scss';
import { Row, Col } from 'react-bootstrap'
const UserDetails = ({ form, setter, onSubmit }) => {
const hanldeOnChange = (e) => {
setter(prev => {
// access property by input element's name
// update the state on parent component
prev[e.target.name] = e.target.value;
return { ...prev } // return copy after updating
})
}
const [listTypeElements, setListTypeElements] = useState({ workExperience: '', academicQualification: '' })
const handleListInput = (property) => {
setter(prev => {
prev[property].push(listTypeElements[property]);
return { ...prev }
})
setListTypeElements(prev => {
prev[property] = ''
return { ...prev }
})
}
const handleRemoveItem = (property) => {
setter(prev => {
prev[property].pop();
return { ...prev }
})
}
return (
<>
<div className='UserDetails'>
<Row>
<Col lg='6'>
<div className='persnlInfo'>
<h4>
Personal Information
</h4>
<p>Your Name</p>
<input type="text" placeholder="Enter here" onChange={hanldeOnChange} name='name' />
<p>Your Contact</p>
<input type="text" placeholder="Enter here" onChange={hanldeOnChange} name='contact' />
<p>Your Address</p>
<textarea className='formAddress' rows="5" cols="10" placeholder="Enter here" onChange={hanldeOnChange} name='address' />
<p id='impLinks'>Important Links</p>
<p>Facebook</p>
<input type="text" placeholder="Enter here" onChange={hanldeOnChange} name='facebook' />
<p>Instagram</p>
<input type="text" placeholder="Enter here" onChange={hanldeOnChange} name='instagram' />
<p>Linkedin</p>
<input type="text" placeholder="Enter here" onChange={hanldeOnChange} name='linkedin' />
</div>
</Col>
<Col lg='6'>
<h4>
Professional Information
</h4>
<p>Objective</p>
<textarea className='formObjective' rows="5" cols="10" placeholder="Enter here" onChange={hanldeOnChange} name='objective' />
<p>Work Experience</p>
{form.workExperience.map((value) =>
<textarea className='formWrkExp' value={value} rows="3" cols="10" disabled={true} />)}
<textarea className='formWrkExp' value={listTypeElements['workExperience']} rows="3" cols="10" placeholder="Enter here" onChange={(e) => setListTypeElements(prev => {
prev['workExperience'] = e.target.value;
return { ...prev }
})} />
< div className='Button' >
<input type='button' value='Add' onClick={() => handleListInput('workExperience')} />
<input type='button' value='Remove' onClick={() => handleRemoveItem('workExperience')} />
</div>
<p id='AcQu'>Academic Qualification</p>
{form.academicQualification.map((value) =>
<textarea className='formAcQu' value={value} rows="3" cols="10" disabled={true} />)}
<textarea className='formAcQu' value={listTypeElements['academicQualification']} rows="3" cols="10" placeholder="Enter here" onChange={(e) => setListTypeElements(prev => {
prev['academicQualification'] = e.target.value;
return { ...prev }
})} />
< div className='Button' >
<input type='button' value='Add' onClick={() => handleListInput('academicQualification')} />
<input type='button' value='Remove' onClick={() => handleRemoveItem('academicQualification')} />
</div>
</Col>
<Row>
<div className='sbmtButton'>
<input type='button' value='Generate CV' onClick={onSubmit} />
</div>
</Row>
</Row>
</div>
</>
)
}
export default UserDetails;

New div doesn't apear after submit React

I have form in react:
let isSubmitted = false
const {
register,
errors,
handleSubmit
} = useForm < FormInput > ()
const onSubmit = (data: FormInput) => {
axios
.post(endpoint, data, {
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
isSubmitted = true
})
.catch(err => {})
}
return (
<div>
<div>
<div>
<form className={classes.root} autoComplete="off">
<Grid style={formGrid} container>
<Grid item xs={6}>
<div style={formItem}>
<input style={inputStyle} placeholder="Name of Provider" name="provider" type="text" ref={register({ required: true })} />
<div style={erroStyle}>{errors.Provider && 'Provider is required'}</div>
</div>
<div style={formItem}>
<input placeholder="Name of Contact Person" name="contactPerson" ref={register({ required: true })} style={inputStyle} />
<div style={erroStyle}>{errors.ContactPerson && 'Contact person is required'}</div>
</div>
<div style={formItem}>
<input placeholder="Contact Email" name="email" ref={register({ required: true })} style={inputStyle} />
<div style={erroStyle}>{errors.Email && 'Contact email is required'}</div>
</div>
<input onClick={handleSubmit(onSubmit)} type="submit" style={submitStyle} />
</Grid>
<Grid item xs={6}>
<div style={formItem}>
<input placeholder="Address (optional)" required name="address" style={inputStyle} ref={register} />
</div>
</Grid>
{isSubmitted &&
<div>Thank you! Form submitted, we'll contact you shortly.</div>}
</Grid>
</form>
</div>
</div>
</div>
) }
on submit I want to show this div: {isSubmitted && <div>Thank you! Form submitted, we'll contact you shortly.</div>} however it doesn't appear. I'm new to react and I'm looking from angular perspective and if it was angular it would work :) what I am missing here?
React will only re-render your component if the state has changed, any update on a variable will not trigger a re-render.
Assuming you are using a function component, you have to use the useState hook to allow your component to re-render:
import React, { useState } from 'react';
// --- snip ---
const [isSubmitted, setIsSubmitted] = useState(false);
const {
register,
errors,
handleSubmit
} = useForm < FormInput > ()
const onSubmit = (data: FormInput) => {
axios
.post(endpoint, data, {
headers: {
'Content-Type': 'application/json'
}
})
.then(res => {
setIsSubmitted(true)
})
.catch(err => {})
}

React, Display message after submitting form with react-hook-form

I use react-hook-form and would like to display message to the user after submitting form. I know how to do that with alert, but would like to have that message as a paragraph. After submitting fields should be again empty.
Here is my Form component:
import React from "react";
import { useForm } from "react-hook-form";
const Form = ({ title }) => {
const { register, handleSubmit, errors } = useForm();
const onSubmit = (data, e) => {
e.preventDefault();
console.log(data);
alert(`thank you ${data.name} for your message`);
};
return (
<div className="formContainer">
<Title title="Lets stay in touch" />
<div className="form">
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form__row">
<input
className={`inputForm ${errors.name ? "inputFormError" : ""}`}
name="name"
type="text"
placeholder="name"
ref={register({ required: true })}
/>
<input
className={`inputForm ${
errors.surname ? "inputFormError" : ""
}`}
name="surname"
type="text"
placeholder="surname"
ref={register({ required: true })}
/>
</div>
<div>
<textarea
className={`inputForm areaForm ${
errors.message ? "inputFormError" : ""
}`}
name="message"
placeholder="Your message"
ref={register({ required: true })}
></textarea>
</div>
<div>
<button className="form__formButton" type="submit">
Send
</button>
</div>
</form>
</div>
</div>
);
};
export default Form;
Pretty simple with just a useState to show the message and reset API from hookForm to reset the form :
import React from "react";
import { useForm } from "react-hook-form";
const Form = ({ title }) => {
const [message, setMessage] = useState('');
const { register, handleSubmit, errors, reset } = useForm();
const onSubmit = (data, e) => {
e.preventDefault();
console.log(data);
setMessage(`thank you ${data.name} for your message`);
reset();
};
return (
<div className="formContainer">
<Title title="Lets stay in touch" />
<div className="form">
{message}
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form__row">
<input
className={`inputForm ${errors.name ? "inputFormError" : ""}`}
name="name"
type="text"
placeholder="name"
ref={register({ required: true })}
/>
<input
className={`inputForm ${
errors.surname ? "inputFormError" : ""
}`}
name="surname"
type="text"
placeholder="surname"
ref={register({ required: true })}
/>
</div>
<div>
<textarea
className={`inputForm areaForm ${
errors.message ? "inputFormError" : ""
}`}
name="message"
placeholder="Your message"
ref={register({ required: true })}
></textarea>
</div>
<div>
<button className="form__formButton" type="submit">
Send
</button>
</div>
</form>
</div>
</div>
);
};
export default Form;

Redirect doesn;t work, while {this.props.history} does

While an action is successful, redirect is not working but history.replace is working.
Why??
import React, { Component } from "react";
import { Formik, Form, Field, ErrorMessage } from "formik";
import * as Yup from "yup";
import { withRouter } from "react-router-dom";
class Login extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div id="loginContainer" className="signinup-container">
<h3 className="mb-4"> Log In </h3>
<Formik
initialValues={{
email: "",
password: "",
rememberMe: false,
error: ""
}}
validationSchema={Yup.object().shape({
email: Yup.string()
.required("Please enter email to login.")
.email("Please enter a valid email."),
password: Yup.string().required("Please enter your password.")
})}
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Logging in", values);
setSubmitting(false);
return <Redirect to="/dashboard" />;
//this.props.history.replace("/dashboard");
//this.props.history.push('/dashboard');
}, 500);
}}
>
{props => {
const {
values,
touched,
errors,
isSubmitting,
handleChange
} = props;
return (
<Form id="loginForm" className="signinupForm" noValidate>
<ErrorMessage
name="error"
component="span"
className="login-error"
/>
<div className="form-group ">
<label className="form-label" htmlFor="email">
Email
</label>
<Field
type={"email"}
name="email"
placeholder="Enter your email"
className={
"form-control" +
(errors.email && touched.email ? " is-invalid" : "")
}
/>
<ErrorMessage
name="email"
component="span"
className="invalid-input"
/>
</div>
{/* Email */}
<div className="form-group position-relative">
<label className="form-label" htmlFor="password">
Password
</label>
<Field
type={"password"}
name="password"
placeholder="Enter your password"
className={
"form-control" +
(errors.password && touched.password ? " is-invalid" : "")
}
/>
<ErrorMessage
name="password"
component="span"
className="invalid-input"
/>
</div>
{/* Password */}
<div className="form-group">
<label className="form-label" htmlFor="rememberMe">
<input
type="checkbox"
id="rememberMe"
name="rememberMe"
onChange={handleChange}
defaultChecked={values.rememberMe}
value={values.rememberMe}
/>
Remember me
</label>
</div>
{/* Rememeber Me */}
{isSubmitting ? (
<span className="loader-gif">loading</span>
) : null}
<button
type="submit"
className="btn btn-filled"
disabled={isSubmitting}
>
Login
</button>
{/*Submit */}
</Form>
);
}}
</Formik>
</div>
);
}
}
export default withRouter(Login);
Please go to login page and check this.
Codesandbox link - https://codesandbox.io/s/winter-hooks-s9vgx
You are calling your Redirect JSX component from onSubmit method. However you cannot do that since you need to return the JSX elements from within the render method which is why you need to use history to update route
onSubmit={(values, { resetForm, setErrors, setSubmitting }) => {
setTimeout(() => {
console.log("Logging in", values);
setSubmitting(false);
this.props.history.replace("/dashboard");
}, 500);
You must be using slash:
to='/dashboard'
As what #Shubham Khatri said, but if you want to use <Redirect> you can create a state and detect if logged and then redirect it, like this.
Changes are adding
this.state = {
isLoggedIn: false
};
And in render
if (this.state.isLoggedIn) return <Redirect to="/dashboard" />;
in onSubmit
this.setState({ isLoggedIn: true });

Resources