DEV Community

loading...

My one-liner event handler function for toggling check boxes state in ReactJS

potouridisio profile image Ioannis Potouridis Updated on ・2 min read

I am a ReactJS developer for over three years and every time I write an event handler function, a fairy dies... or one developer
scratches his head.

This is a post on how I shortened my toggling handler functions with the help of mathematics and utility libraries down to one line.

This is a typical ReactJS component which renders five names and each one is paired by a checkbox.

const names = ["Gale", "James", "Kim", "Mindy", "Karen"];

export function Names() {
  const [selected, setSelected] = React.useState(names);

  const handleToggle = (name) => () => {
    //
    //
  }

  return (
    <fieldset>
      <legend>Names</legend>
      {names.map((name) => (
        <React.Fragment key={name}>
          <input
            checked={selected.includes(name)}
            id={name}
            onChange={handleToggle(name)}
            type="checkbox"
          />
          <label htmlFor={name}>{name}</label>
          <br />
        </React.Fragment>
      ))}
    </fieldset>
  );
}

The most logical thing to do in our handleToggle event handler is to check the selected array and if it includes the name then it should filter that out, else it should add it to the array.

And the implementation should look like this:

const handleToggle = (clickedName) => () => {
  if (selected.includes(clickedName)) {
    setSelected(selected.filter((name) => name !== clickedName));
  } else {
    setSelected([...selected, clickedName]);
  }
};

This implementation is fine, it will pass code reviews any day due to readability.

But... I'm weird.

What if we change the clicked name into an array instead of a string and... return the symmetric difference of the two arrays?

For example, the symmetric difference of ["Gale", "James", "Kim", "Mindy", "Karen"] and ["Gale"] is ["James", "Kim", "Mindy", "Karen"]. It works like our filter, right?

Also the symmetric difference of ["James", "Kim", "Mindy", "Karen"] and ["Gale"] is ["James", "Kim", "Mindy", "Karen", "Gale"] which works like our array concatenation.

The symmetric difference can also be expressed with the XOR operator and as soon as I've read XOR and been a fan of utility libraries I started using the xor utility function.

Then the handler started looking like this:

// import xor from 'lodash/xor';

const handleToggle = (clickedName) => () => {
  setSelected(xor(selected, [clickedName]));
};

But like I said... I'm weird.

I read about ramda and lodash's fp and got curried away.

Every utility function exported from those two modules has reversed arguments (iteratee-first data-last) which means it can be called like this:

xor([clickedName], selected);

They are also auto-curried (did you catch the pun earlier?
) which means they can be written like this:

xor([clickedName])(selected);

Which means if you pass the first function as an argument to the setSelected action it will be called with the selected state anyway because set state actions accept callbacks that are called with the previous state.

So my handlers look like this now:

// import xor from 'lodash/fp/xor';

const handleToggle = (clickedName) => () => setSelected(xor([clickedName]));

What do you think?

Did you scratch your head?

Discussion

pic
Editor guide
Collapse
jamesncox profile image
James Cox

I have no idea what the “xor” operator is but I am now determined to learn more. Thanks for this and a very interesting read!