Write better components using hooks
Let’s take an example of a table component that allows actions like selection, sorting, etc.:
There are two approaches to implementing it:
Controlled — the benefits are full control over the component’s state and the ability to intercept actions and state. The downside is a lot of boilerplate to use the component as is.
Uncontrolled — the benefit is that it just works without added boilerplate. But the problem is that we have no knowledge or control over its internal state.
The Solution — component + controller (with hooks!)
We separate the controlled component, which is just the view layer, from its state-manager, or ‘controller’ (yikes, I just used view-controller terminology :))
Here is the component, which is completely controlled, and gets everything using props:
export const DataTable = ({ data, selection, sorting, setSelection, setSorting, ... })
And the logic is separated to a custom hook:
export const useDataTableState = (...) => {
const [selection, setSelection] = useState(initialSelection);
const [sorting, setSorting] = useState(initialSorting);
...
}
We return it as an object, so we can spread it directly to the component’s props:
return {
selection,
setSelection,
sorting,
setSorting,
...
}
And now, we can use the component and its state manager as is, and enjoy the best of all worlds:
import { DataTable, useDataTableState } from ‘data-table’;const MyComp = () => {
const dataTableState = useDataTableState();
return <DataTable {...dataTableState} data={ … }/>;
}
The useDataTableState function does all the wiring for us.
The added-value is that we are in control of the data table state, and we can use it, or decide to intercept some of its values/actions.
Like this example:
import { DataTable, useDataTableState } from ‘data-table’;const MyComp = () => {
const dataTableState = useDataTableState();return (<div>
<DataTable {...dataTableState} data={ … }/>
<span>Selected { size(dataTableState.selection) } rows</span>
<button onClick={ dataTableState.setSelection({}) }>Clear selection</button>
</div>);
}
Here is the complete pseudo-implementation:
export const DataTable = ({ data, selection, sorting, setSelection, setSorting, ... }) => (
// this is the controlled component implementation
...
)export const useDataTableState = (initialSelection = {}, initialSorting = null, ...) => {
// here we implement the state management logic
const [selection, setSelection] = useState(initialSelection);
const [sorting, setSorting] = useState(initialSorting);
...
return {
selection,
setSelection,
sorting,
setSorting,
...
}
}
This approach could be beneficial for both open source writers, and for writing reusable components in your company.