combineHooks
combineHooks combines several hooks (or any [get, set] pairs) into one. The
combined getter returns the value of the first hook; the combined setter
invokes all setters with the same argument.
Sometimes one piece of state is represented in more than one place in the DOM.
A classic example is a todo item whose “done” status is both the checked
property of its checkbox and a strike-through on its text.
Without combineHooks, keeping the two places in sync is manual:
function Todo({ text }) { const [checkbox, textSpan] = useRefs();
const [isDone] = useCheckbox(checkbox); const [, setLineThrough] = useStyleBoolean( textSpan, 'text-decoration-line', 'line-through', null, );
function handleDone(event) { setLineThrough(event.target.checked); }
return ( <li> <input type="checkbox" onChange={handleDone} ref={checkbox} /> <span ref={textSpan}>{text}</span> </li> );}Every time the state changes, we must remember to touch the second place too.
combineHooks lets you declare the relationship instead:
function Todo({ text }) { const [checkbox, textSpan] = useRefs();
const [isDone, setDone] = combineHooks( useCheckbox(checkbox), useStyleBoolean(textSpan, 'text-decoration-line', 'line-through', null), );
function handleDone(event) { setDone(event.target.checked); }
return ( <li> <input type="checkbox" onChange={handleDone} ref={checkbox} /> <span ref={textSpan}>{text}</span> </li> );}Now setDone(...) updates the checkbox and the strike-through in one call,
and isDone() still reads from the checkbox.
Reading and writing
Section titled “Reading and writing”- The combined getter returns the result of the first hook’s getter.
- The combined setter invokes every hook’s setter with the same argument.
Because the getter comes from the first hook, put the “source of truth” first.
In the example above, the checkbox is declared as the source of truth and the
strike-through is declared as a projection of it. When the user toggles the
checkbox, the browser updates its checked property natively and the handler
propagates the change to the text. When something else calls setDone(false)
— say, a “reset all” button — both places are updated.
Initial values
Section titled “Initial values”Hooks can be initialized from props like any other element:
function Todo({ text, done = false }) { // ... return ( <li> <input type="checkbox" checked={done} onChange={handleDone} ref={checkbox} /> <span ref={textSpan}>{text}</span> </li> );}The checked attribute becomes the initial value of the checkbox, and
isDone() reads it back.