This is a select-all checkbox (and checkboxes that it controls), that's usually found in tables/lists.
Two things I'm concerned about:
I'm returning elements from a React Hook - is that OK to do? As opposed to create a Component (probably a render prop)
I'm calculating
master.indeterminate
in auseEffect
, as opposed to doing it when the checkboxes are clicked.
I'm planning on using it in a table that will have 1000s of entries, filtered/paginated upto 100.
I realize #2 could be a performance issue since I'm essentially checking all checkboxes to determine whether to set master.indeterminate
, but since the table will be filtered I want this to be "reactive" as well.
Any other issues I might be overlooking?
const h = (...args) => React.createElement(...args)
function useSelectAll(existingStateOrSize = []) {
const [state, update] = React.useState(existingStateOrSize)
const master = React.useRef(null)
const children = React.useRef(state.map(() => null))
function setIndeterminate () {
/* Calculate master.indeterminate */
let allChecked
for (const child of children.current) {
if (!child) continue
const checked = Boolean(child.checked)
if (allChecked === undefined) {
allChecked = checked
} else if (allChecked !== checked) {
allChecked = null
break
}
}
if (typeof allChecked === 'boolean') {
master.current.checked = allChecked
master.current.indeterminate = false
} else {
master.current.indeterminate = true
}
}
function onChangeMaster() {
children.current.forEach(child => child.checked = master.current.checked)
}
function onChangeChild(e) {
const i = children.current.indexOf(e.target)
const newState = Array.from(state)
newState[i] = e.target.checked
update(newState)
}
React.useEffect(setIndeterminate)
return [
state,
h('input', { type: 'checkbox', ref: master, onChange: onChangeMaster }),
state.map((x, i) => h('input', { type: 'checkbox', ref: r => children.current[i] = r, checked: existingStateOrSize[i], onChange: onChangeChild }))
]
}
function Table() {
const [state, master, children] = useSelectAll([null,null,null])
return h('table', {}, [
h('tr', {}, h('th', {}, master), h('th', {}, 'Select all')),
h('tr', {}, h('td', {}, children[0]), h('td', {}, 'First')),
h('tr', {}, h('td', {}, children[1]), h('td', {}, 'Second')),
h('tr', {}, h('td', {}, children[2]), h('td', {}, 'Third')),
])
}
ReactDOM.render(h(Table), document.getElementById('container'))