Reputation: 622
I have a React component that renders a checkbox. The problem is it takes two clicks to turned to checked and then two clicks to change to unchecked.
JS:
import React, { Component } from "react";
class CheckboxRow extends Component {
constructor(props) {
super(props);
this.state = { checked: false };
}
inputChangedHandler() {
this.setState({ checked: !this.state.checked });
}
render() {
return (
<div className="checkbox-row">
<input
id={this.props.id}
type="checkbox"
onChange={() => this.inputChangedHandler()}
checked={this.state.checked}
/>
<label htmlFor={this.props.id} className="checkbox-row__label">
Label goes here
</label>
</div>
);
}
}
export default CheckboxRow;
So what do i need to do so that the checkbox turns to checked on one click and changes back to unchecked on one click?
Upvotes: 10
Views: 8822
Reputation: 89
I am picking up on the previous discussion as I am facing the same issue, but removing prevent.default does not change anything:
Basically, I am using react hook form to do advanced filterings and multiples selection:
The "checkbox" inputs requires two click to really be checked even if the onChange event is capture at first click:
import classes from "./JobsFiltering.module.css";
import {
useEffect,
useState
} from "react";
import Link from "next/link";
import Image from "next/image";
import React from "react";
// import Pagination from "../Pagination";
import SearchIcon from "@mui/icons-material/Search";
import JobsList from "./JobsList";
import {
useForm,
Controller
} from "react-hook-form";
import {
TextField,
Checkbox
} from "@mui/material";
import MuiAutoComplete from "./LimitTags";
import SliderSalary from "./SliderSalary";
import CheckBoxWorkTypes from "./CheckBoxWorkTypes";
export default function JobsFiltersBox(props) {
// const { jobs } = props;
const [jobs, setJobs] = useState([]);
// REACT HOOK FORM SET UP
const defaultValues = {
select: "",
input: "",
mode: "onChange",
reValidateMode: "onChange",
};
const {
register,
handleSubmit,
control,
formState: {
errors
},
} = useForm({
defaultValues,
});
// SEARCH JOBS ASYNC FUNCTION API CALL
async function searchJobs(formProperties = {}) {
return fetch("/api/jobs/queries", {
method: "POST",
body: JSON.stringify(formProperties),
headers: {
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((json) => setJobs(json))
.catch((error) => console.log(error));
}
// LISTEN TO FILTERS
async function onChange(data, event) {
// event.preventDefault();
console.log(data);
searchJobs({
experienceLevels: data.seniority,
remoteTypes: data.remoteTypes,
workTypes: data.workTypes,
tags: data.tags,
terms: data.terms,
salaryRanges: data.salaryRanges,
sortBy: data.sortBy,
});
}
// ALLOW A CALL TO SEARCH JOB WHEN COMPONENT DID MOUN - AT INITIALIZATION
useEffect(() => {
searchJobs();
}, []);
// ****
// FIEDS OPTIONS DEFINITION
const seniorities = ["Junior", "Intermediate", "Senior"];
const remoteTypes = ["Full Remote", "Hybrid", "No Remote"];
const workTypes = [
"Full-time",
"Part-time",
"Internship",
"Contractor",
"Cofounder",
];
return ( <
section className = {
classes["section-jobs-filter"]
} >
<
div className = {
classes["global-box"]
} > { /* LEFT SIDE */ } { /* LEFT SIDE TO BE PUT HERE WHEN READY */ } <
div className = {
classes["sidebar-box"]
} >
<
form onChange = {
handleSubmit(onChange)
}
className = {
classes["sidebar-form"]
} >
{ /* SEARCH BAR */ } <
div className = {
classes["filter-card"]
} >
<
label className = {
classes.label
}
htmlFor = "filter" >
Filters <
/label> <
input type = "search"
placeholder = "Search.."
className = {
classes["input-main-filters"]
} { ...register("terms", {})
}
/> <
/div>
{ /* Seniority Level */ } <
div className = {
classes["box-category"]
} >
<
div className = {
classes.row
} >
<
h4 className = {
classes.label
} > Seniority < /h4> <
/div> <
div className = {
classes["filter-list"]
} > {
seniorities.map((optionName, i) => ( <
React.Fragment key = {
optionName
} >
<
label key = {
optionName
}
className = {
classes.filter
} >
<
input type = "checkbox"
value = {
optionName
}
name = "seniority" { ...register("seniority", {})
}
// className={
// data.seniority.include({ optionName })
// ? "activated"
// : "toggledButtonId"
// }
/> {
optionName
} <
/label> <
/React.Fragment>
))
} <
/div> <
/div>
{ /* remoteType */ } <
div className = {
classes["box-category"]
} >
<
div className = {
classes.row
} >
<
h4 className = {
classes.label
} > Remote Work < /h4> <
/div> <
div className = {
classes["filter-list"]
} > {
remoteTypes.map((c, i) => ( <
React.Fragment key = {
c
} >
<
label key = {
c
}
className = {
classes.filter
} >
<
input type = "checkbox"
value = {
c
}
name = "remoteTypes" { ...register("remoteTypes", {})
}
/> {
c
} <
/label> <
/React.Fragment>
))
} <
/div> <
/div>
{ /* workType */ } <
div className = {
classes["box-category"]
} >
<
div className = {
classes.row
} >
<
h4 className = {
classes.label
} > Work Type < /h4> <
/div> <
div className = {
classes["filter-list"]
} > {
workTypes.map((workTypeName, i) => ( <
React.Fragment key = {
workTypeName
} >
<
label key = {
workTypeName
}
className = {
classes.filter
} >
<
input type = "checkbox"
value = {
workTypeName
}
name = "workTypes" { ...register("workTypes", {})
}
/> {
workTypeName
} <
/label> <
/React.Fragment>
))
} <
/div> <
/div>
{ /* <CheckBoxWorkTypes control={control} /> */ }
{ /* Compensation range */ } { /* Not operational yet */ } <
SliderSalary control = {
control
}
/> { /* Location : city or country ? */ } { /* <CountrySelect control={control} /> */ } { /* Tags - with "voir plus" options ? */ } <
div className = {
classes["filter-list"]
} >
<
MuiAutoComplete control = {
control
}
/> <
/div> <
/form> <
/div>
{ /* RIGHT SIDE */ } <
div className = {
classes["jobs-list-box"]
} >
<
div className = {
classes["sort-by"]
} >
<
form onChange = {
handleSubmit(onChange)
} >
<
div className = {
classes["select-wrapper"]
} >
<
span > Sort by: < /span> <
select { ...register("sortBy")
}
className = {
classes["sort-by--select"]
}
id = "select" >
<
option value = "date" > Date < /option>
`` <
option value = "compensation" > Compensation < /option> { /* Later on: most view, hottest, etc.. */ } <
/select> <
/div> <
/form>
{ /* */ } <
/div>
<
div className = {
classes["jobs-list"]
} >
<
div className = {
classes["jobs-list"]
} >
<
JobsList jobs = {
jobs
}
/> <
/div> <
/div> {
/* <div className={classes["box-pagination"]}>
<Pagination />
</div> */
} <
/div> <
/div> <
/section>
);
}
See line 120 for an example: https://codepen.io/Marving972/pen/PoagGNP?editors=0010
Upvotes: -1
Reputation: 1547
You should not access the old state directly when altering the state, like
this.setState({ checked: !this.state.checked });
instead do
this.setState(({ checked }) => ({ checked: !checked }));
Or you access the passed event from the onChange. Here written as a shorter functional component
function CheckboxRow({ id }) {
const [checked, setChecked] = useState(false);
const onChange = event => {
event.persist();
setChecked(event.target.checked);
};
return (
<div className="checkbox-row">
<input id={id} type="checkbox" onChange={onChange} checked={checked} />
<label htmlFor={id} className="checkbox-row__label">
Label goes here
</label>
</div>
);
}
Upvotes: 8
Reputation: 622
After staring at it for hours i had a eureka moment! I had a prevent.default()
in the form onChange
! Removing the prevent.default()
fixed it Thanks to those that have replied.
Upvotes: 27