Last active
June 6, 2020 15:43
-
-
Save janjakubnanista/919e8be7447edacdd6f9558237b1aa3c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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