React Hook Form set checkbox to checked state - javascript

I am trying out React-Hook-form
The simple code for the checkbox is as below:
import React from 'react'
import { useForm } from 'react-hook-form'
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm()
const onSubmit = (data: any) => console.log(data)
console.log(errors)
return (
<div className='mx-auto justify-center p-32 flex'>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='p-2'>
<label htmlFor=''>January</label>
<input
type='checkbox'
placeholder='January'
{...register('January', {})}
className='mx-3'
checked
/>
</div>
<div className='p-2'>
<label htmlFor=''>February</label>
<input
type='checkbox'
placeholder='February'
{...register('February', {})}
className='mx-3'
/>
</div>
<input type='submit' />
</form>
</div>
)
}
I can submit the form correctly but I have like the January checkbox to start off as a checked box but when I put 'checked' as shown in the code, I somehow could not 'uncheck' it.
I seem to be missing something and any help would be greatly appreciated.

The issue with passing checked is that it takes control away from useForm to manage the checkbox.
Imagine the function register() returns { checked: true/false, onChange: changeHandler }. So if we where to look at the attributes this produces the following.
<input
type='checkbox'
placeholder='January'
{...register('January', {})}
className='mx-3'
checked
/>
<input
type='checkbox'
placeholder='January'
{...{
checked: true/false,
onChange: changeHandler,
}}
className='mx-3'
checked
/>
<input
type='checkbox'
placeholder='January'
checked={true/false}
onChange={changeHandler}
className='mx-3'
checked
/>
Since checked is present twice, the latter will override the former. In this case your checked is last so it overrides the value that is managed by useForm.
Passing it before the register() call won't help you either, since your default value will be overwritten by a value managed by useForm and is therefore never used.
Now that I've cleared up why this issue happens let's move on to the solution.
useForm allows you to pass a default values when you initially call the hook.
const {
register,
handleSubmit,
formState: { errors },
} = useForm({ defaultValues: { January: true } });
// ...
<input
type='checkbox'
{...register("January")}
className='mx-3'
/>
Alternatively, instead of giving each checkbox its own name, you could also use "months". If there are multiple checkboxes using the same name, the result will not be true/false, but rather an array containing the values.
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: { months: ["January"] }
// only January ^ is checked by default
});
//...
<input
type='checkbox'
value='January'
{...register("months")}
className='mx-3'
/>
<input
type='checkbox'
value='February'
{...register("months")}
className='mx-3'
/>

The complete working code based on #3limin4tOr
import React, { useState } from 'react'
import { useForm } from 'react-hook-form'
export default function App() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: { months: ['January'] },
})
const onSubmit = (data: any) => console.log(data)
console.log(errors)
return (
<div className='mx-auto justify-center p-32 flex'>
<form onSubmit={handleSubmit(onSubmit)}>
<div className='p-2'>
<label htmlFor=''>January</label>
<input
type='checkbox'
value='January'
placeholder='January'
{...register('months')}
className='mx-3'
/>
</div>
<div className='p-2'>
<label htmlFor=''>February</label>
<input
type='checkbox'
value='February'
placeholder='February'
{...register('months')}
className='mx-3'
/>
</div>
<input type='submit' />
</form>
</div>
)
}

You can find here input, checkbox, and radio in one field with React Hook form
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import "./App.css";
import { FormProvider, useForm, useFormContext } from "react-hook-form";
const ChooseCarType = () => {
const { register } = useFormContext();
return (
<>
<div>
<label htmlFor="field-rain">
<input
{...register("weather")}
type="radio"
value="rain"
id="field-rain"
/>
Rain
</label>
<label htmlFor="field-wind">
<input
{...register("weather")}
type="radio"
value="wind"
id="field-wind"
/>
Lots of wind
</label>
<label htmlFor="field-sun">
<input
{...register("weather")}
type="radio"
value="sun"
id="field-sun"
/>
Sunny
</label>
</div>
</>
);
};
const ChooseService = () => {
const { register } = useFormContext();
return (
<>
<div>
<label>
<input type="checkbox" {...register("jan")} />
Jan
</label>
<label>
<input type="checkbox" {...register("feb")} />
Feb
</label>
<label>
<input type="checkbox" {...register("mar")} />
Mar
</label>
</div>
</>
);
};
const UserData = () => {
const { register } = useFormContext();
return (
<>
<div>
<label>
Username
<input {...register("username")} />
</label>
<label>
Password
<input {...register("password")} />
</label>
</div>
</>
);
};
function App() {
const methods = useForm();
const onSubmit = (data: any) => alert(JSON.stringify(data, null, 2));
return (
<FormProvider {...methods}>
<form onSubmit={methods.handleSubmit(onSubmit)}>
<ChooseCarType />
<ChooseService />
<UserData />
<button type="submit">Submit</button>
</form>
</FormProvider>
);
}
export default App;

Related

Error Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'name') in React-hook-form

To validate the form, I decided to use the react-hook-form library, when you select the last name in the input in the console, an error occurs as in the screenshot. The input itself is a dadata library and I prescribe all the necessary attributes for the react-hook-form to the component, I think that's why the error occurs, what should I do? And how can this be resolved? I removed all the unimportant part of the code if that.
error screenshot
export default function Fio () {
const { register, formState: { errors } } = useForm();
const [value, setValue] = useState();
return (
<>
<span id="FIO">ФИО</span>
<FioSuggestions
token={token}
value={value}
onChange={setValue}
{...register( "fio",{required: true})}/><br/>
<div style={{height: 40}}>
{errors?.fio && <p>Error</p>}
</div>
</>
)
}
export default function Form () {
const {handleSubmit} = useForm();
const onSubmit = (data) => {
alert(JSON.stringify(data))
}
return(
<form id="formAdv" onSubmit={handleSubmit(onSubmit)}>
<Fio/>
<Birthday/><br/>
<Phone/><br/>
<label>
<input type="radio"
name="gender"
value="MALE"
/> Мужчина
<input type="radio"
name="gender"
value="FEMALE"
/> Женщина
</label><br/>
<Multiselector/><br/>
<Selector/><br/>
<label>
<input type="checkbox"/> Не отправлять СМС.
</label><br/>
<input type="submit"/>
</form>
)
}

How to use Link and Route inside of condition term

So I am trying to move through pages in react, my goal is when I did validate the things I need (name, and number) the page will switch and I will be in another route. (without refresh the page).
I tried to do it with window.location but its refreshing the page
I cant use <Link> because I want to switch route only after the validation (inside IF condition) or I can and I don't know-how.
my code :
import React, {useState} from 'react'
import {Link} from 'react-router-dom';
export default function Signup(props) {
const [name, setName] = useState(' ')
const [number, setNumber] = useState(' ')
const [forklift, setForklift] = useState(false)
const [styleNumber,setStyleNumber]= useState({
display:'none',
})
const [styleName,setStyleName]= useState({
display:'none',
})
let validNum=false;
let validName=false;
let driverLicense=()=>{
if(forklift === 'true'){
setForklift(true)
}
else{
setForklift(false)
}
if(number.length<5 || number.length>5){
setStyleNumber({
display:'block',
color:'red'
})
}
else{
validNum=true;
}
if(name.indexOf(' ')==-1|| name.length<4){
setStyleName({
display:'block',
color:'red'
})
}
else{
validName=true;
}
if(validNum && validName){
props.addWorker(name,number,forklift)
let myBtn=document.getElementById('button').innerHTML=<Link to='/'></Link>
console.log(myBtn)
}
else{
alert('Error')
}
}
return (
<div>
<h2>Sign up</h2>
<label>No.</label>
<input onChange={(e)=>{setNumber(e.target.value)}} type='number' maxLength='5'></input><br />
<br /> <p style={styleNumber}> the number must be with 5 digits.</p>
<label>Full Name:</label> <input onChange={(e)=>{setName(e.target.value)}} ></input><br /> <br />
<p style={styleName} >the name must contain minimum 4 characters.</p>
<label>Forkligt truck</label> <br /> <br />
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' value="true"/>
<label >Yes</label><br/>
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' checked value="false"/>
<label >no</label><br /> <br />
<button id='button' onClick={driverLicense}>Create</button>
</div>
)
}
I think what you want to achieve is to Redirect the page when you meet a condition
in this case
import React, {useState} from 'react'
import {Redirect} from 'react-router-dom';
export default function Signup(props) {
const [name, setName] = useState(' ')
const [isVerified, setIsVerified = useState(false);
const [number, setNumber] = useState(' ')
const [forklift, setForklift] = useState(false)
const [styleNumber,setStyleNumber]= useState({
display:'none',
})
const [styleName,setStyleName]= useState({
display:'none',
})
let validNum=false;
let validName=false;
let driverLicense=()=>{
if(forklift === 'true'){
setForklift(true)
}
else{
setForklift(false)
}
if(number.length<5 || number.length>5){
setStyleNumber({
display:'block',
color:'red'
})
}
else{
validNum=true;
}
if(name.indexOf(' ')==-1|| name.length<4){
setStyleName({
display:'block',
color:'red'
})
}
else{
validName=true;
}
if(validNum && validName){
props.addWorker(name,number,forklift)
setIsVerified(true);
}
else{
alert('Error')
}
}
if (isVerified) {
return <Redirect to="/" />
}
return (
<div>
<h2>Sign up</h2>
<label>No.</label>
<input onChange={(e)=>{setNumber(e.target.value)}} type='number' maxLength='5'></input><br />
<br /> <p style={styleNumber}> the number must be with 5 digits.</p>
<label>Full Name:</label> <input onChange={(e)=>{setName(e.target.value)}} ></input><br /> <br />
<p style={styleName} >the name must contain minimum 4 characters.</p>
<label>Forkligt truck</label> <br /> <br />
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' value="true"/>
<label >Yes</label><br/>
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' checked value="false"/>
<label >no</label><br /> <br />
<button id='button' onClick={driverLicense}>Create</button>
</div>
)
}
This should work:
export default function Signup(props) {
const [redirect,setRedirect]=useState(false);
const[path,setPath]=useState("");
if(condition)
{
setPath(set your path here)
setRedirect(true);
}
return (
{redirect?<Redirect to={path}/>:null}
<div>
<h2>Sign up</h2>
<label>No.</label>
<input onChange={(e)=>{setNumber(e.target.value)}} type='number' maxLength='5'></input><br />
<br /> <p style={styleNumber}> the number must be with 5 digits.</p>
<label>Full Name:</label> <input onChange={(e)=>{setName(e.target.value)}} ></input><br /> <br />
<p style={styleName} >the name must contain minimum 4 characters.</p>
<label>Forkligt truck</label> <br /> <br />
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' value="true"/>
<label >Yes</label><br/>
<input onClick={(e)=>{setForklift(e.target.value)}} type="radio" name='Forklift' checked value="false"/>
<label >no</label><br /> <br />
<button id='button' onClick={driverLicense}>Create</button>
</div>
)
}
The best way is to move pages without refreshing is by wrap your components with a higher-order component. Using HOC you can use context and change what you want to render. Refer documentation

How to make use of Radio Group with useFormik Hook

I am trying to get useFormik to validate radio groups, but it seems not to work, here is a brief example of what I am doing, whenever I submit the form after checking any of the radio input, formik throws validation error, ({currState:"you must choose property state"}), even though I choose an option.
I realized getFieldProps attaches value field to the radio, so i tried using defaultValue then react throws an error about choosing one of controlled and uncontrolled components.
import { useFormik } from "formik"
export function ListProperty(){
const { handleSubmit, getFieldProps, touched, errors } = useFormik(
{
initialValues: {
currState:"",
},
validationSchema:Yup.object().shape({
currState:Yup.string().required("you must choose property state")
}),
return (
<form onSubmit={handleSubmit} >
<div className="form-group inline">
<div className="form-control">
<input type="radio"
name="currState"
{...getFieldProps("currState")}
value="serviced"
/>
<label>serviced</label>
</div>
<div className="form-control">
<input
type="radio"
value="furnished"
name="currState"
{...getFieldProps("currState")}
/>
<label>furnished</label>
</div>
<div className="form-control">
<input
type="radio"
value="newlybuilt"
name="currState"
{...getFieldProps("currState")}
/>
<label>newly built</label>
</div>
</div>
<button type="submit">submit </button>
</form>
)
}
I gave up on the implementation with getFieldProps and did it simpler, like this:
import { useFormik } from 'formik'
export default function Component() {
const formik = useFormik({
initialValues: {
radioButtonValue: ''
},
onSubmit: values => console.log(values)
})
const handleRadioButtons = e => formik.values.radioButtonValue = e.target.value
return (
<form onSubmit={formik.handleSubmit}>
<input
type="radio"
id="one"
name="group"
value="One"
onChange={e => handleRadioButtons(e)}
required
/>
<label htmlFor="one">One</label>
<br />
<input
type="radio"
id="two"
name="group"
value="Two"
onChange={e => handleRadioButtons(e)}
/>
<label htmlFor="two">Two</label>
<button type="submit">Submit</button>
</form>
)
}
Beware, if you use formik.values.radioButtonValue value as a useEffect dependency, then setting it like this: formik.values.radioButtonValue = e.target.value not gonna trigger the change, and useEffect won't launch (at least in my case it didn't). As an alternative, you gonna have to implement some kind of condition check with this value in your useEffect code
You need to map onChange like below.
<input
type="radio"
value="furnished"
name="currState"
onChange={getFieldProps("currState").onChange}
/>

How to check/uncheck a list of checkboxes in react

I have a room page and in that page I have a list of sensors attached to that room, those sensors can be selected using a checkbox, like so:
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
value={s.id}
checked={s.roomId === values.id}
onChange={handleCheckbox}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
the problem is - this approach prohibits me from unchecking the checkbox (so if in db that sensor is attached to that room - that's it). How could I rewrite this so that I can check/uncheck this checkbox?
in the class you must have state for that,
a sample would be somewhat like this
export default class yourComponent extends React.Component {
state = {
checkedBoxes: []
}
handleCheckbox = (e, s) => {
const checkedBoxes = [...this.state.checkedBoxes];
if(e.target.checked) {
checkedBoxes.push(s)
} else {
const index = checkedBoxes.findIndex((ch) => ch.roomId === s.roomId);
checkedBoxes.splice(index, 1);
}
this.setState({checkedBoxes});
}
render() {
return(
<div className="checkboxRowContent">
{sensors.map(s => {
return (
<div className="checkboxElementWrapper" key={s.id}>
<label htmlFor={`sensor${s.id}`}>
<div className="checkboxLabel">
<Link to={`/sensors/edit/${s.id}`}>{s.name}</Link>
</div>
<input
type="checkbox"
id={`sensor${s.id}`}
name="sensorId"
checked={checkedBoxes.find((ch) => ch.roomId === s.roomId)}
onChange={(e) => handleCheckbox(e, s)}
/>
<span className="checkbox" />
</label>
</div>
);
})}
</div>
)
}
}
A state, checkedBoxes for getting all selected checkboxes.
A handler handleCheckbox for handling checkbox clicks,
You have handleCheckBox and a controlled component. We don't see what you do in the event handler but when it's controlled, you can check it by altering your sensors array (if in state/props) so s.roomId === values.id will be true.
If you don't want it to be controlled, you can probably use defaultChecked which will let you work with it in a different way.
see https://reactjs.org/docs/forms.html#controlled-components
import React, {Component} from 'react';
import axios from 'axios';
const Books = props=>(
<div className='form-group'>
<label>{props.book}
<input type='checkbox' name={props.name} className='form-check' onChange={props.onChange} />
</label>
</div>
)
class Total extends Component{
constructor(props){
super(props);
this.onChangeCheck = this.onChangeCheck.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.state={
checkBoxes: [],
books:[]
}
}
componentDidMount() {
axios.get('http://localhost:3000/api/book/').then(resolve=>{
console.log(resolve.data.data);
this.setState({
books:resolve.data.data
}).catch(err=>{
console.log(err)
})
})
}
onChangeCheck(e){
console.log(e.target.name)
if(e.target.checked){
const array = this.state.checkBoxes;
array.push(e.target.name)
this.setState({
checkBoxes:array
})
}else{
const array = this.state.checkBoxes;
const index = array.indexOf(e.target.name);
console.log(index)
array.splice(index,1);
console.log(array);
this.setState({
checkBoxes:array
})
}
}
onSubmit(e){
e.preventDefault();
axios.put("http://localhost:8080/books/getTotal/",this.state.checkBoxes).then(resolve=>{
console.log(resolve)
alert(`Total price of books ${resolve.data}`);
}).catch(err=>{
console.log(err);
})
}
render(){
return(
<div className='card'>
<div className='card-header'>
</div>
<div className='card-body'>
<form className='form' onSubmit={this.onSubmit}>
<div className='form-group'>
{
this.state.books.map(object=>(
<Books name={object._id} book={object.name} onChange={this.onChangeCheck} />
)
)
}
</div>
<div className='form-group'>
<button type='submit' className='btn btn-success'>Get Total</button>
</div>
</form>
</div>
</div>
)
}
}
export default Total;

Keep code DRY on create/edit form

I have a few inputs that are used in my form for both create and update. I decided to make them a component.
// used for CRU on the event record
import React from 'react';
class Form extends React.Component {
render() {
return (
<div className="slds-form">
<div className="slds-form-element">
<label className="slds-form-element__label">Assigned To</label>
<div className="slds-form-element__control">
<input ref={(input) => this.assigned = input} type="text" className="slds-input" disabled/>
</div>
</div>
<div className="slds-form-element">
<label className="slds-form-element__label">Related To</label>
<div className="slds-form-element__control">
<input ref={(input) => this.related = input} type="text" className="slds-input" disabled/>
</div>
</div>
<div className="slds-form-element">
<label className="slds-form-element__label">Location</label>
<div className="slds-form-element__control">
<input ref={(input) => this.location = input} type="text" className="slds-input" />
</div>
</div>
<div className="slds-form-element">
<label className="slds-form-element__label">Event Start</label>
<div className="slds-form-element__control">
<input ref={(input) => this.start = input} type="text" className="slds-input" />
</div>
</div>
<div className="slds-form-element">
<label className="slds-form-element__label">Event End</label>
<div className="slds-form-element__control">
<input ref={(input) => this.end = input} type="text" className="slds-input" />
</div>
</div>
<div className="slds-form-element">
<label className="slds-form-element__label">Contact</label>
<div className="slds-form-element__control">
<input ref={(input) => this.contact = input} type="text" className="slds-input" disabled/>
</div>
</div>
<button type="button" className="slds-button slds-button--neutral">Cancel</button>
<button type="submit" className="slds-button slds-button--brand">{this.props.buttonLabel}</button>
</div>
);
}
}
export default Form;
I then attempted to use this component in my <Create /> component.
// used for Create on the event record
import React from 'react';
import Form from './Form';
class Create extends React.Component {
createEvent(e) {
console.log("createEvent() has fired.");
e.preventDefault();
const event = {
assigned: this.assigned.value,
related: this.related.value,
location: this.location.value,
start: this.start.value,
end: this.end.value,
contact: this.contact.value
}
console.log(event);
}
render() {
return (
<form onSubmit={(e) => this.createEvent(e)}>
<Form buttonLabel="Create" />
</form>
);
}
}
export default Create;
When I try to hit the Create button on my <Create /> component I get an error
Uncaught TypeError: Cannot read property 'value' of undefined
at Create.createEvent (webpack:///./src/components/Event/Create.js?:42:32)
at onSubmit (webpack:///./src/components/Event/Create.js?:59:27)
at Object.ReactErrorUtils.invokeGuardedCallback (webpack:///./~/react/lib/ReactErrorUtils.js?:70:16)
at executeDispatch (webpack:///./~/react/lib/EventPluginUtils.js?:89:21)
at Object.executeDispatchesInOrder (webpack:///./~/react/lib/EventPluginUtils.js?:112:5)
at executeDispatchesAndRelease (webpack:///./~/react/lib/EventPluginHub.js?:44:22)
at executeDispatchesAndReleaseTopLevel (webpack:///./~/react/lib/EventPluginHub.js?:55:10)
at Array.forEach (native)
at forEachAccumulated (webpack:///./~/react/lib/forEachAccumulated.js?:25:9)
at Object.processEventQueue (webpack:///./~/react/lib/EventPluginHub.js?:231:7)
I then check the console and see the refs belong in my <Form /> component, and not my <Create /> component.
Is there a way to pass the refs from my child component, <Form />, to its parent, <Create />?
That's a lot of refs! Good news, you really don't need them, at all. As a very very general rule, you should only be using refs if you are interacting with an external library that doesn't "understand" React (d3, Greensock, TinyMCE, etc).
Tackling it in an uncontrolled way can be done like:
const User = (props) => (
<div>
<input name="foo" className="form-control" />
<input name="foo2" className="form-control" />
<button type="submit" className="btn btn-primary">{props.buttonLabel}</button>
</div>
);
class App extends React.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
}
onChange(e) {
this.setState({
[e.target.name]: e.target.value,
});
}
onSubmit(e) {
e.preventDefault();
console.log(this.state);
}
render() {
return (
<div className="container">
<br />
<form onChange={this.onChange} onSubmit={this.onSubmit}>
<User buttonLabel="Create"/>
</form>
</div>
);
}
};
ReactDOM.render(<App />, document.getElementById('app'));
Codepen example:
http://codepen.io/cjke/pen/zNXxga?editors=0010

Categories

Resources