How to make the checkbox such that if the header checkbox is checked then all other checkboxes will be checked and unchecked if all other checkboxes will be unchecked? Also if other checkboxes are selected then the header checkbox should be undeterminate using react with typescript?
I have the following code in App.tsx
import React, {useState} from "react";
import { useQuery } from "react-query";
import { GraphQLClient, gql } from "graphql-request";
import "./index.css";
import Button from "./Components/Button";
const API_URL = `https://graphqlzero.almansi.me/api`;
const graphQLClient = new GraphQLClient(API_URL, {
headers: {
Authorization: `Bearer ${process.env.API_KEY}`,
},
});
export function useGetPosts() {
return useQuery("get-posts", async () => {
const {
users: { data },
} = await graphQLClient.request(gql`
{
users {
data {
id
name
username
email
address {
street
}
phone
website
}
}
}
`);
return data;
});
}
const App = () => {
const [checked, setChecked] = useState(false)
const handleClick = () => setChecked(!checked)
const response = useGetPosts();
const { data } = response;
console.log({ response });
// address: {street: 'Kulas Light'}
// email: "[email protected]"
// id: "1"
// name: "Leanne Graham"
// phone: "1-770-736-8031 x56442"
// username: "Bret"
// website: "hildegard.org"
return (
<div>
<section>
<div className="header">Users
<form>
<input
type="text"
placeholder="Search Here" />
</form>
<Button name = "New User" />
</div>
<header>
<div className="col" >
<input
onClick={handleClick} checked={checked}
type="checkbox"
className = "column"
/>
</div>
<div className="col">Name</div>
<div className="col">Username</div>
<div className="col">Email</div>
<div className="col">Phone</div>
<div className="col">Website</div>
<div className="col">Address</div>
</header>
{data?.map((item: any) => (
<>
<div className="row">
<div className="col">
<input
onClick={handleClick} checked={checked}
type="checkbox" />
</div>
<div className="col">{item?.name}</div>
<div className="col">{item?.username}</div>
<div className="col">{item?.email}</div>
<div className="col">{item?.phone}</div>
<div className="col">{item?.website}</div>
<div className="col">{item?.address?.street}</div>
</div>
</>
))}
</section>
</div>
);
};
export default App;
CodePudding user response:
This example may help you: https://chakra-ui.com/docs/components/form/checkbox#indeterminate
Basically, there're only 2 rules like you listed:
- The header checkbox is checked only if all other checkboxes are checked (we test this by using .every())
- The header checkbox is indeterminate only if there are just some checkboxes are checked (we test this by using .some())
CodePudding user response:
I noticed that you have the same onClick function for both the header input and the children inputs. In order to make this work, we need to create another handleClick function for the children inputs.
Create an array of boolean values
const isCheckedData = data.map((item)=>{ return false; });
Add another state which would hold this array
const [isCheckedArray, setIsCheckedArray] = useState(isCheckedData);
Distribute the values of the array to your mapped data below
{data?.map((item: any, index) => ( <>
<div className="row">
<div className="col">
<input
onClick={() => handleClickChildren(index)} checked={isCheckedArray[index]}
type="checkbox" />
</div>
<div className="col">{item?.name}</div>
<div className="col">{item?.username}</div>
<div className="col">{item?.email}</div>
<div className="col">{item?.phone}</div>
<div className="col">{item?.website}</div>
<div className="col">{item?.address?.street}</div>
</div>
</>
))}
- Create a function that will take the index as an argument so that we could change the value of a specific checkbox in our state.
const handleClickChildren = (index) => {
const findCheckBox = isCheckedArray.find(data=>isCheckedArray. indexOf(data)===index);
const newCheckedArray = isCheckedArray.splice(isCheckedArray.indexOf(findCheckBox), 1, !findCheckBox);
setIsCheckedArray(newCheckedArray);
Check the docs for splice for more info, but basically what that does is it just replaces the previous value to true or false. https://www.w3schools.com/jsref/jsref_splice.asp
5.) Lastly, Refactor your first handleOnClick function to change the state of the isCheckedArray depending if the header checkbox is set to true or false.
CodePudding user response:
Example in javascript, with react where you have the functionality that you want.
import React, { useEffect, useState } from 'react';
const CHECKBOX_VALUES = [false, false];
function Question16() {
const [checkboxValues, setCheckboxValues] = useState(CHECKBOX_VALUES);
const checkboxRef = React.useRef(null);
const onCheck = (pos, value) => {
const nextCheckboxValues = [ ...checkboxValues ];
nextCheckboxValues[pos] = value;
setCheckboxValues(nextCheckboxValues);
}
useEffect(() => {
const isIndeterminate = checkboxRef.current.indeterminate;
const someCheckboxChecked = checkboxValues.some((checkValue) => checkValue);
const someCheckboxUnchecked = checkboxValues.some((checkValue) => !checkValue);
const nextIndeterminate = someCheckboxChecked && someCheckboxUnchecked;
if (isIndeterminate !== nextIndeterminate) {
checkboxRef.current.indeterminate = nextIndeterminate;
}
if (!nextIndeterminate) {
checkboxRef.current.checked = someCheckboxChecked;
}
}, [checkboxValues]);
return (
<>
<div>
<span>Parent checkbox</span>
<input type="checkbox" ref={checkboxRef} />
</div>
<div>
<span>Checkbox 1</span>
<input
checked={checkboxValues[0]}
onChange={(elem) => {
const value = elem.target.checked;
onCheck(0, value);
}}
type="checkbox"
/>
</div>
<div>
<span>Checkbox 2</span>
<input
checked={checkboxValues[1]}
onChange={(elem) => {
const value = elem.target.checked;
onCheck(1, value);
}}
type="checkbox"
/>
</div>
</>
);
}
export default Question16;