LOTUSMS
LOTUSMS

Reputation: 10240

handling a select all in react and materialUI

I have several sets of checkboxes that should work independently and a toggle that toggles them all (within their groups only). I have two states (one for the toggle and one for the checkboxes). I can identify the checkbox I'm clicking with the event.target.value, that way I can manipulate it individually. But I'm having trouble controlling them all at once with the toggle as well as making the toggle come active when someone independently checks them all true.

In summary

1- when the toggle is on, all checkboxes within its group come on, same for off

2- When I turn on each checkbox individually until they are all on, the toggle turns on and when I uncheck one of them, the toggle turns off

I've made a sandbox for you to play on. Thanks in advance

const [active, setActive] = useState(false)
const [singleactive, setSingleActive] = useState([])

const handleSwitch = (e) => {
  if(e.target.value === "Select All") {
    setActive(e.target.checked)
    setSingleActive([...singleactive])
  } else {
    setSingleActive([])
  }
}

const handleSingleSwitch = (e) => {
  const index = singleactive.indexOf(e.target.value)

  if(index === -1) {
    setSingleActive([...singleactive, e.target.value])
  } else {
    setSingleActive(singleactive.filter((singleactive) => singleactive !== e.target.value))
  }
}  

Upvotes: 1

Views: 456

Answers (1)

LOTUSMS
LOTUSMS

Reputation: 10240

Well, I figure it out. Though I find answering your own question a bit pretentious lol, here it is in case it helps

Updated states and functions

const [active, setActive] = useState(false)
/// fill a new array with false values for the length of the data to load them all unchecked
const [checkedState, setCheckedState] = useState(new Array(data.length).fill(false));

const handleSwitch = (e) => {
  if(e.target.checked) {
    /// if the toggle event comes back checked, set the active state to true and re-fill the array with all true values which is what Select All does
    setActive(true)
    setCheckedState(new Array(data.length).fill(true));
  } else {      
    /// if the toggle event comes back unchecked, set the active state to false and re-fill the array with all false values which is what Deselect All does
    setActive(false)
    setCheckedState(new Array(data.length).fill(false));
  }
}


const handleOnChange = (position) => {
  // every time you click an infividual checkbox, map through the state and compare its index with the position in the array. If it's true, make it true otherwise false; then set the state with this value
  const updatedCheckedState = checkedState.map((item, index) => {
    return (
      index === position ? !item : item
    )}
  );
  setCheckedState(updatedCheckedState);
  
  /// if the new generated array of values contains at least one false in it, set the active class on the toggle to false, but if there isn't at least one false, then all are true, so set the active class to true on the toggle
  if(updatedCheckedState.includes(false)) {
    setActive(false)
  } else {
    setActive(true)
  }
};

I also removed the value on the toggleAll checkbox that I inadvertently set statically to Select All. This way I can control it via state

<Stack direction="row" spacing={1} alignItems="center">
   <Typography>Deselect All</Typography>
   <Switch 
      size="small"
      checked={active}
      onChange={handleSwitch} />
    <Typography>Select All</Typography>
 </Stack>

And lastly the checkboxes

<FormControlLabel 
  control={
    <Checkbox 
      size="small"
      name={item.toLowerCase()} 
      value={item.toLowerCase()} 
      checked={checkedState[index]}
      onChange={() => handleOnChange(index)}
    />
  } 
  label= {item.replaceAll('_', ' ')} />

Check the sandbox for the updated code.

Upvotes: 1

Related Questions