react-hooks-forms and default checked radio button - reactjs

I am using react hooks forms and has tried most thing to default check a radio button:
<FormGroup tag="fieldset">
<FormGroup check>
<Label check>
<Input type="radio" name="isIBAN" innerRef={register} defaultChecked/> IBAN
</Label>
</FormGroup>
<FormGroup check>
<Label check>
<Input type="radio" name="isIBAN" innerRef={register} value={false} /> BIC/SWIFT
</Label>
</FormGroup>
</FormGroup>
I dont know if you can do it from the default values:
const { register, handleSubmit, errors, setValue, getValues } = useForm(
{
defaultValues:{
accountNr: "XXXXKB20201555555555",
bic:"XXXDKKK",
isIBAN:true
}
}
);
How is the correct way to default the first radio button to checked? I could make another hook but there must be some built in in react hooks forms?

Please take a look at the codesandbox demo for the radio group example.
const defaultValues = {
Native: "",
TextField: "",
Select: "",
ReactSelect: { value: "vanilla", label: "Vanilla" },
Checkbox: false,
switch: false,
RadioGroup: "female", // default value is the radio input value
numberFormat: 123456789,
downShift: "apple"
};
<section>
<label>Radio Group</label>
<Controller
as={
<RadioGroup aria-label="gender">
<FormControlLabel
value="female"
control={<Radio />}
label="Female"
/>
<FormControlLabel
value="male"
control={<Radio />}
label="Male"
/>
</RadioGroup>
}
name="RadioGroup"
control={control}
/>
</section>

Related

Validating radio button with React Hook Form

I have a custom radio button component in React looking like this:
export default function SingleSelectionButton(props) {
const {
values, name, required, errors, defaultData,
xs, md, register, infoBox, infoBoxContent, validateField } = props;
return (
<>
<Form.Group>
<Form.Label>
<FormattedMessage
id={name}
/>
</Form.Label>
{values.map((value) => (
<span key={value} className="SingleSelectionButton">
<input
{...register(name, { required: required}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
<label htmlFor={name + value}>
<FormattedMessage id={value} />
</label>
</span>
))}
</Form.Group>
</>
);
};
I call it like this, using an array for the different values:
<SingleSelectionButton
name="us_person"
md={6}
values={["yes", "no"]}
register={register}
required={true}
errors={errors}
validateField="no"
/>
The validation with required is working fine.
The problem is that I don't manage to validate a value in particular.
I have tried the following:
<input
{...register(name, { required: required, validate: value => value === validateField })}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
And also:
<input
{...register(name, { required: required, pattern: validateField })}
id={name + value}
type="radio"
name={name}
value={value}
defaultChecked={defaultData.primary[name] === value}
/>
So far no joy. What am I missing?

react-datetime formik validation

I'm using react-datetime compenent in my react-bootstrap form. Formik with Yup is used for validation.
import React from 'react';
import { Container, Form, Button, Alert, Row, Col, InputGroup } from "react-bootstrap";
import "react-datetime/css/react-datetime.css";
import { Formik} from 'formik';
import * as Icon from 'react-bootstrap-icons';
import * as yup from "yup"
import Datetime from 'react-datetime';
import moment from 'moment';
const validDOB = function( current ){
return current.isBefore(moment());
};
const schema = yup.object().shape({
userId: yup.string().required('Please enter a valid User ID'),
userName: yup.string().required('User\'s Name cannot be empty'),
userDOB: yup.string().required('User\'s Date of Birth cannot be empty'),
});
function AddWorkload(){
return (
<>
<Container>
<Row className="justify-content-md-center">
<h3 style={{textAlign: 'center'}}>Add a New Workload</h3>
<Formik
validationSchema={schema}
onSubmit={console.log}
initialValues={{
userId: '',
userName: '',
userDOB: '',
}}
>
{({
handleSubmit,
handleChange,
handleBlur,
values,
touched,
isValid,
errors,
setFieldValue,
}) => (
<Col md lg="6">
<Form noValidate onSubmit={handleSubmit}>
<Form.Group controlId="userIdInput">
<Form.Label>User ID</Form.Label>
<InputGroup>
<Form.Control
placeholder="User ID"
type="text"
name="userId"
value={values.userId}
onChange={handleChange}
isInvalid={!!errors.userId}
aria-describedby="userIdHelpBlock"
/>
<Button variant="dark"><Icon.Search /> Find User</Button>
<Form.Control.Feedback type="invalid">
{errors.userId}
</Form.Control.Feedback>
</InputGroup>
<Form.Text id="userIdHelpBlock" muted>
Please click "Find User" to fill out the details.
</Form.Text>
</Form.Group>
<Form.Group controlId="userName">
<Form.Label>User Name</Form.Label>
<Form.Control
placeholder="User Name"
type="text"
name="userName"
value={values.userName}
onChange={handleChange}
isInvalid={!!errors.userName}
/>
<Form.Control.Feedback type="invalid">
{errors.userName}
</Form.Control.Feedback>
</Form.Group>
<Form.Group controlId="userDOB">
<Form.Label>Date of Birth</Form.Label>
<Datetime
inputProps={{
placeholder: 'DOB',
id: 'userDOB',
name: 'userDOB',
}}
dateFormat="DD-MM-YYYY"
timeFormat={false}
value={values.userDOB}
isValidDate={validDOB}
onChange={(dateFromValue) => {
setFieldValue('userDOB', dateFromValue);
}
}
isInvalid={!!errors.userDOB}
/>
<Form.Control.Feedback type="invalid">
{errors.userDOB}
</Form.Control.Feedback>
</Form.Group>
<div style={{clear: 'both'}}></div>
<br></br>
<div className='text-center'>
<Button variant="dark" type="reset">Reset Form</Button>{' '}
<Button variant="success" type="submit">Save</Button>
</div>
</Form>
</Col>
)}
</Formik>
</Row>
</Container>
</>
);
}
export default AddWorkload;
Validation for userId and userName is working properly. But I can't get the validation to work for userDOB. I also tried yup.date() but it doesn't work. react-datetime uses moment.js. So, what type to use for the schema and how to get the validation working for that compenent.
Any help would be appreciated.
Thanks
I found my issue. Its because Form.Control.Feedback doesn't correspond to the component Datetime.
So, I removed the Form.Control.Feedback and replaced that with a custom div to display the error message for the Datetime component.
<Form.Group controlId="patientDOB">
<Form.Label>Date of Birth</Form.Label>
<Datetime
inputProps={{
placeholder: 'DD-MM-YYYY',
id: 'userDOB',
name: 'userDOB',
className: formik.errors.userDOB && formik.touched.userDOB ? "form-control is-invalid": "form-control"
}}
closeOnSelect={true}
dateFormat="DD-MM-YYYY"
timeFormat={false}
value={formik.values.userDOB}
isValidDate={validDOB}
onChange={(dateFromValue) => {
formik.setFieldValue('userDOB', moment(dateFromValue).format('DD-MM-YYYY'));
}
}
renderInput={(props) => {
return <input {...props} value={(formik.values.userDOB) ? props.value : ''} />
}}
/>
{formik.errors.userDOB && formik.touched.userDOB ? (
<div className="text-danger" style={{fontSize: '0.875em'}}>{ formik.errors.userDOB }</div>
) : null}
</Form.Group>

react-hook-form and MUI FormControl

So I am trying to register some radio buttons for my form, however it will not register.
<FormControl component="fieldset" inputRef={register({ required: true })}>
<FormLabel component="legend">Promoting</FormLabel>
<RadioGroup aria-label="promoting" name="promoting" value={promoting} onChange={handleChange} inputRef={register({ required: true })}>
<FormControlLabel value="business" control={<Radio />} label="Business" />
<FormControlLabel value="nonprofit" control={<Radio />} label="Non-Profit" />
<FormControlLabel value="event" control={<Radio />} label="Event" />
</RadioGroup>
</FormControl>
I am wondering what I am doing wrong
Full code:
export default function BuildAd() {
const [promoting, setValue] = React.useState('female');
const handleChange = (event) => {
setValue(event.target.value);
};
const { register, handleSubmit, errors } = useForm();
const onSubmit = data => console.log(data);
console.log(errors);
return (
<div style={{ padding: 16, margin: 'auto', maxWidth: 600 }}>
<CssBaseline />
<Typography variant="h5" align="center" component="h2" gutterBottom>
Create your campaign
</Typography>
<Paper style={{ padding: 16 }}>
<form onSubmit={handleSubmit(onSubmit)}>
<input type="text" placeholder="First name" name="First name" ref={register({required: true, maxLength: 80})} />
<input type="text" placeholder="Last name" name="Last name" ref={register({required: true, maxLength: 100})} />
<input type="text" placeholder="Email" name="Email" ref={register({required: true, pattern: /^\S+#\S+$/i})} />
<input type="tel" placeholder="Mobile number" name="Mobile number" ref={register({required: true, minLength: 6, maxLength: 12})} />
<select name="Title" ref={register({ required: true })}>
<option value="Mr">Mr</option>
<option value="Mrs">Mrs</option>
<option value="Miss">Miss</option>
<option value="Dr">Dr</option>
</select>
<input name="Developer" type="radio" value="Yes" ref={register({ required: true })}/>
<input name="Developer" type="radio" value="No" ref={register({ required: true })}/>
<Typography variant="h5">What are you Advertising</Typography>
<Box m={2}/>
<FormControl component="fieldset" inputRef={register({ required: true })}>
<FormLabel component="legend">Promoting</FormLabel>
<RadioGroup aria-label="promoting" name="promoting" value={promoting} onChange={handleChange} inputRef={register({ required: true })}>
<FormControlLabel value="business" control={<Radio inputRef={register({ required: true })} />} label="Business" />
<FormControlLabel value="nonprofit" control={<Radio inputRef={register({ required: true })} />} label="Non-Profit" />
<FormControlLabel value="event" control={<Radio inputRef={register({ required: true })} />} label="Event" />
</RadioGroup>
</FormControl>
<input type="submit" />
</form>
</Paper>
</div>
);
}
You can fallback on controllable components by using Controller if it doesn't work.
In your case, I suspect it's because the name props passed to your RadioGroup is not the same as the name attribute when passed into native inputs like input or select.
react-hook-form V7
const { handleSubmit, control } = useForm({
defaultValues: {
promoting2: '',
},
});
<FormControl component="fieldset">
<FormLabel component="legend">Promoting</FormLabel>
<Controller
rules={{ required: true }}
control={control}
name="promoting2"
render={({ field }) => (
<RadioGroup {...field}>
<FormControlLabel
value="business"
control={<Radio />}
label="Business"
/>
<FormControlLabel
value="nonprofit"
control={<Radio />}
label="Non-Profit"
/>
<FormControlLabel
value="event"
control={<Radio />}
label="Event"
/>
</RadioGroup>
)}
/>
</FormControl>
react-hook-form V6
const { register, handleSubmit, errors, control } = useForm({
promoting: '',
});
const onSubmit = (data) => alert(JSON.stringify(data, null, 2));
<form onSubmit={handleSubmit(onSubmit)}>
<FormControl component="fieldset">
<FormLabel component="legend">Promoting</FormLabel>
<Controller
rules={{ required: true }}
control={control}
name="promoting"
as={
<RadioGroup>
<FormControlLabel
value="business"
control={<Radio />}
label="Business"
/>
<FormControlLabel
value="nonprofit"
control={<Radio />}
label="Non-Profit"
/>
<FormControlLabel
value="event"
control={<Radio />}
label="Event"
/>
</RadioGroup>
}
/>
</FormControl>
<input type="submit" />
</form>
If you want to read the value when onChange fires, you cannot do that when passing the element to the as props:
<Controller
{...}
as={
<MyComponent
onChange={handleChange} // handleChange will never be called
/>
}
/>
The reason is because Controller will copy the element that you pass in and pass its own onChange props that will override yours. See here and here.
To fix that problem, you need to use another way to pass the element down by using render props to gain more control on how you should render your components.
<Controller
rules={{ required: true }}
control={control}
name="promoting2"
render={({ name, onBlur, onChange, value }) => (
<RadioGroup
value={value}
onBlur={onBlur}
onChange={(e) => {
onChange(e);
console.log(e.target.value); // will be called this time
}}
>
<FormControlLabel
value="business"
control={<Radio />}
label="Business"
/>
{...}
</RadioGroup>
)}
/>

How to add Radio Button in Formik Validations Reactjs?

I am using Formik for Validating my Registration form I want to add validate gender via radio button how can I do that. I am not able to add radio button.
This is what I have done:-
const SignupSchema = Yup.object().shape({
email: Yup.string()
.email('Invalid email')
.required('Required'),
password: Yup.string()
.min(4, 'Password Must be four characters long!')
.max(20, 'Too Long!')
.required('Required'),
});
class Register extends Component {
render() {
return (
<Formik
initialValues={{
email: '',
password:'',
gender:'',
}}
validationSchema={SignupSchema}
onSubmit={values => {
console.log(values);
}}
>
{({ errors, touched }) => (
<Form>
<Field style={customStyles.textfield} placeholder="Email" name="email" type="email" />
{errors.email && touched.email ? <div}>{errors.email}</div> : null}
<Field placeholder="Enter Password" name="password" type="password" />
{errors.password && touched.password ? <div >{errors.password}</div> : null}
<button type="submit">Submit</button>
</Form>
)}
</Formik>
</div>
)
}
}
change gender initialValues as male
<Field
name="gender"
render={({ field }) => (
<>
<div className="radio-item">
<input
{...field}
id="male"
value="male"
checked={field.value === 'male'}
name="type"
type="radio"
/>
<label htmlFor="male">Male</label>
</div>
<div className="radio-item">
<input
{...field}
id="female"
value="female"
name="type"
checked={field.value === 'female'}
type="radio"
/>
<label htmlFor="female">Female</label>
</div>
</>
)}
/>
This solution gives you the chance to have more than 2 options.
Let say you have a file called Main.js and you want to put a radio button input in it. First, provide a list of options for your radio button in the Main.js
const radioOptions = [
{ key: 'Option 1', value: 'value 1' },
{ key: 'Option 2', value: 'value 2' },
{ key: 'Option 3', value: 'value 3' }
];
Next, create a RadioButton.js file with below code
import React from 'react';
import { Field } from 'formik';
const RadioButton = (props) => {
const { label, name, options, ...rest } = props;
return (
<div>
<label htmlFor={name}>{label}</label>
<Field name={name} {...rest} >
{
({ field }) => {
return options.map(option => {
return (
<React.Fragment key={option.key}>
<input
type='radio'
id={option.id}
{...field}
value={option.value}
checked={field.value === option.value}
/>
<label htmlFor={option.id}>{option.key}</label>
</React.Fragment>
);
})
}
}
</Field>
</div>
);
};
export default RadioButton;
Then put the reusable RadioButton Component in the Main.js wherever you want the radio button input to render.
The result UI will be 3 radio buttons with values "value 1", "value 2" and "value 3".
You can check out this awesome youtube series to know more.
This can be done simply but using this code below. it worked for me:
You will need to import Formik from "formik"
<Formik
initialValues={{
email: '',
password:'',
gender:'',
}}
// validations
validationSchema={SignupSchema}
onSubmit={values => { console.log(values); }}
>
{(formik) => (
<Form onSubmit={formik.handleSubmit}>
//Your Other inputs........
<div className="custom-control">
<input
id="male"
type="radio"
value="male"
name='gender'
onChange={formik.handleChange}
defaultChecked={formik.values.gender=== "male"}
/>
<label
className="custom-control-label"
htmlFor="male"
>
Male
</label>
</div>
<div className="custom-control">
<input
id="female"
type="radio"
value="female"
name='gender'
onChange={formik.handleChange}
defaultChecked={formik.values.gender=== "female"}
/>
<label
className="custom-control-label"
htmlFor="female"
>
Female
</label>
</div>
//Your Other inputs......
<button type="submit">Submit</button>
</Form>
)}
</Formik>

semantic-ui-react: Does large dataset in Form.Select makes the Form slow?

I am having below form(inside modal) created using semantic-ui-react.
<Modal open={editBasicModal} size="small">
<Modal.Header>Your basic details</Modal.Header>
<Modal.Content scrolling>
<Form loading={isSubmitting}>
<Form.Group inline widths="equal">
<Form.Input
required
label="First Name"
fluid
type="text"
name="firstName"
value={values.firstName}
onChange={handleChange}
error={errors.firstName !== undefined}
/>
<Form.Input
required
label="Last Name"
fluid
type="text"
name="lastName"
value={values.lastName}
onChange={handleChange}
error={errors.lastName !== undefined}
/>
</Form.Group>
<Form.TextArea
label="Bio"
type="text"
name="bio"
value={values.bio}
onChange={handleChange}
rows={3}
error={errors.bio !== undefined}
/>
<Form.Select
label="Country"
name="location.country"
placeholder="Country"
value={values.location.country}
onChange={(e, { value }) => {
setFieldValue("location.country", value);
}}
options={this.state.allCountries}
/>
</Form>
</Modal.Content>
<Modal.Actions open={true}>
<Button type="submit" onClick={handleSubmit} >
Update
</Button>
</Modal.Actions>
</Modal>
The above code is from a component which uses Formik + yup.
this.state.allCountries is an array of 200+ records. Now this is making my form slow, the typing inside textarea and input are very slow.
As per my findings the large dataset in the Form.Select is causing the issue, because if i replace the options={this.state.allCountries} to options={[ { key: 1, value: "india", text: "india"} ]}, everything starts working fine. Or if I delete the Form.Selectthen also form works fine.
Few questions?
Is it a known issue?
what are the possible solutions?
I figured out that this is a problem with Form.Select. I changed it with select and everything worked smoothly then. Here is the updated code for select:
<Form.Field >
<label htmlFor="location.country">Country</label>
<select
name="location.country"
id="location.country"
value={values.location.country }
onChange={event => {
setFieldValue("location.country", event.target.value);
}}
>
<option key={0} value={undefined}>
-select-
</option>
{this.state.allCountries}
</select>
</Form.Field>
This renders similar(somewhat) select element with no slowness issue.
Hope it would help someone.

Resources