The type of data objects in the list.
The key in each object that uniquely identifies it (must be of type string or number).
Object containing selection control functions and state:
toggleSelectAll: Toggles selection state of all items.isSelected.something: true if at least one item is selected.isSelected.everything: true if all items in the list are selected.selectedData: A map of currently selected items, keyed by their unique identifier.selectItem: Selects a specific item.deselectItem: Deselects a specific item.deselectAll: Clears all selected items.const {
toggleSelectAll,
isSelected,
selectedData,
selectItem,
deselectItem,
deselectAll
} = useSelection(users, 'user_id');
<button onClick={toggleSelectAll}>
{isSelected.everything ? 'Unselect All' : 'Select All'}
</button>
{users.map(user => (
<div key={user.user_id}>
<input
type="checkbox"
checked={!!selectedData[user.user_id]}
onChange={() =>
selectedData[user.user_id]
? deselectItem(user)
: selectItem(user)
}
/>
{user.name}
</div>
))}
export const useSelection = <
D extends ObjectType,
K extends keyof D = StringNumberKeys<D>,
>(data: D[], key: K): {
toggleSelectAll: () => void;
isSelected: {
something: boolean;
everything: boolean;
};
selectedData: Record<string | number, D>;
selectItem: (item: D) => void;
deselectItem: (item: D) => void;
deselectAll: () => void;
} => {
const emptyState = {} as HashMapType<D, K>
const [selectedData, setSelectedData] = useState(emptyState)
const isSelected = useMemo(() => {
const selectedKeys = Object.keys(selectedData)
return {
something: selectedKeys.length > 0,
everything: data.length > 0 && selectedKeys.length >= data.length && data.every(item => {
return asKey(item[key]) in selectedData
})
}
}, [selectedData, data])
const toggleSelectAll = useCallback(() => {
if (isSelected.everything) {
setSelectedData(prevState => {
const newState = { ...prevState }
data.forEach(item => {
delete newState[asKey<D[K]>(item[key])];
})
return newState
})
} else {
setSelectedData(prevState => {
const newState = { ...prevState }
data.forEach(item => {
newState[asKey<D[K]>(item[key])] = item
})
return newState
})
}
}, [data, selectedData])
const selectItem = useCallback((item: D) => {
setSelectedData(prevState => {
const id = asKey(item[key])
return {
...prevState,
[id]: item
}
})
}, [])
const deselectItem = useCallback((item: D) => {
setSelectedData(prevState => {
const newState = { ...prevState }
delete newState[asKey<D[K]>(item[key])]
return newState
})
}, [])
const deselectAll = useCallback(() => {
setSelectedData(emptyState)
}, [])
return {
toggleSelectAll,
isSelected,
selectedData,
selectItem,
deselectItem,
deselectAll
}
}
A custom React hook that provides selection logic for a list of items.
This hook allows you to manage selected items using their unique key. It provides utilities for selecting, deselecting, toggling selection, and checking selection state.