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?
Top comments (1)
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!