Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save janjakubnanista/919e8be7447edacdd6f9558237b1aa3c to your computer and use it in GitHub Desktop.
Save janjakubnanista/919e8be7447edacdd6f9558237b1aa3c to your computer and use it in GitHub Desktop.
// We can no longer destructure the props - after desctructuring the link
// between our multiple prop and the value/onChange props would vanish
export function Select<T>(props: SelectProps<T>) {
const { idFromValue, itemComponent: ItemComponent, labelFromValue } = props;
// We now "normalize" the props that can take different forms; value and onChange
//
// First we always convert the selected value(s) into an array.
//
// I hope you'll excuse my nested ternary operators and poor choice of a data structure,
// it's been done keeping the article length in mind
const selectedValues = props.multiple ? props.value || [] : props.value === undefined ? [] : [props.value];
const selectedIds = selectedValues.map(idFromValue);
const isSelected = (id: string | number) => selectedIds.includes(id);
// Then we create a toggle even handler based on the value of the multiple prop
const handleToggle = props.multiple
? // In the multiple version we will add/remove the item from the array of selected values
(item: T) => {
const id = idFromValue(item);
const wasSelected = isSelected(id);
// If the value was already selected we will remove it from the array
// otherwise we append it to the end
const newValue = wasSelected
// If the value was already selected we will remove it from the array
? selectedValues.filter(v => idFromValue(v) !== id)
// If it was not selected we append it to the array
: [...selectedValues, item];
props.onChange(newValue);
}
: // In the single version we just call onChange with the toggled item
props.onChange;
return (
<div>
{props.items.map(item => {
const id = idFromValue(item);
const selected = isSelected(id);
const label = labelFromValue(item);
return (
<ItemComponent key={id} value={item} selected={selected} onToggle={handleToggle}>
{label}
</ItemComponent>
);
})}
</div>
);
}
// ...
// Optional properties are a pain when it comes to type narrowing
// and will often produce cryptic errors. That's why defined multiple
// prop as required in both single and multiple versions.
//
// We however don't want to be repeating multiple={false} for all those Selects
// we have created before we had the multiple prop.
Select.defaultProps = {
multiple: false;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment