snippetjavascriptTip
Advanced React state hooks
Viewed 0 times
hooksstateadvancedreact
Problem
React's toolbox is intentionally quite limited, providing you some very versatile building blocks to create your own abstractions. But, if you find
Starting off with a simple one, the
The implementation is rather simple, as well. You use the
The
Using the
useState() too limited for your needs, and useReducer() doesn't quite cut it either, how do you go about creating more advanced state management hooks? Let's deep dive into some advanced state management hooks.Starting off with a simple one, the
useToggler hook provides a boolean state variable that can be toggled between its two states. Instead of managing the state manually, you can simply call the toggleValue function to toggle the state.The implementation is rather simple, as well. You use the
useState() hook to create the value state variable and its setter. Then, you create a function that toggles the value of the state variable and memoize it, using the useCallback() hook. Finally, you return the value state variable and the memoized function.The
Map object is a very versatile data structure in JavaScript, but it's not directly supported by React's state management hooks. The useMap hook creates a stateful Map object and a set of functions to manipulate it.Using the
useState() hook and the Map() constructor, you create a new Map from the initialValue. Then, you use the useMemo() hook to create a set of non-mutating actions that manipulate the state variable, using the state setter to create a new Map every time. Finally, you return the map state variable and the created actions.Solution
const useToggler = initialState => {
const [value, setValue] = React.useState(initialState);
const toggleValue = React.useCallback(() => setValue(prev => !prev), []);
return [value, toggleValue];
};
const Switch = () => {
const [val, toggleVal] = useToggler(false);
return <button onClick={toggleVal}>{val ? 'ON' : 'OFF'}</button>;
};
ReactDOM.createRoot(document.getElementById('root')).render(
<Switch />
);The implementation is rather simple, as well. You use the
useState() hook to create the value state variable and its setter. Then, you create a function that toggles the value of the state variable and memoize it, using the useCallback() hook. Finally, you return the value state variable and the memoized function.The
Map object is a very versatile data structure in JavaScript, but it's not directly supported by React's state management hooks. The useMap hook creates a stateful Map object and a set of functions to manipulate it.Using the
useState() hook and the Map() constructor, you create a new Map from the initialValue. Then, you use the useMemo() hook to create a set of non-mutating actions that manipulate the state variable, using the state setter to create a new Map every time. Finally, you return the map state variable and the created actions.Similar to
useMap, the useSet hook creates a stateful Set object and a set of functions to manipulate it. The implementation is very similar to the previous hook, but instead of using a Map, you use a Set.Similar to the previous hook, we might also need a hook that provides a default value if the state is
null or undefined. The useDefault hook does exactly that. It creates a stateful value with a default fallback if it's null or undefined, and a function to update it.The approach is very similar to the previous hook. You use the
useState() hook to create the stateful value. Then, you check if the value is either null or undefined. If it is, you return the defaultState, otherwise you return the actual value state, alongside the setValue function.Code Snippets
const useToggler = initialState => {
const [value, setValue] = React.useState(initialState);
const toggleValue = React.useCallback(() => setValue(prev => !prev), []);
return [value, toggleValue];
};
const Switch = () => {
const [val, toggleVal] = useToggler(false);
return <button onClick={toggleVal}>{val ? 'ON' : 'OFF'}</button>;
};
ReactDOM.createRoot(document.getElementById('root')).render(
<Switch />
);const useMap = initialValue => {
const [map, setMap] = React.useState(new Map(initialValue));
const actions = React.useMemo(
() => ({
set: (key, value) =>
setMap(prevMap => {
const nextMap = new Map(prevMap);
nextMap.set(key, value);
return nextMap;
}),
remove: (key, value) =>
setMap(prevMap => {
const nextMap = new Map(prevMap);
nextMap.delete(key, value);
return nextMap;
}),
clear: () => setMap(new Map()),
}),
[setMap]
);
return [map, actions];
};
const MyApp = () => {
const [map, { set, remove, clear }] = useMap([['apples', 10]]);
return (
<div>
<button onClick={() => set(Date.now(), new Date().toJSON())}>Add</button>
<button onClick={() => clear()}>Reset</button>
<button onClick={() => remove('apples')} disabled={!map.has('apples')}>
Remove apples
</button>
<pre>
{JSON.stringify(
[...map.entries()].reduce(
(acc, [key, value]) => ({ ...acc, [key]: value }),
{}
),
null,
2
)}
</pre>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(
<MyApp />
);const useSet = initialValue => {
const [set, setSet] = React.useState(new Set(initialValue));
const actions = React.useMemo(
() => ({
add: item => setSet(prevSet => new Set([...prevSet, item])),
remove: item =>
setSet(prevSet => new Set([...prevSet].filter(i => i !== item))),
clear: () => setSet(new Set()),
}),
[setSet]
);
return [set, actions];
};
const MyApp = () => {
const [set, { add, remove, clear }] = useSet(new Set(['apples']));
return (
<div>
<button onClick={() => add(String(Date.now()))}>Add</button>
<button onClick={() => clear()}>Reset</button>
<button onClick={() => remove('apples')} disabled={!set.has('apples')}>
Remove apples
</button>
<pre>{JSON.stringify([...set], null, 2)}</pre>
</div>
);
};
ReactDOM.createRoot(document.getElementById('root')).render(
<MyApp />
);Context
From 30-seconds-of-code: advanced-react-state-hooks
Revisions (0)
No revisions yet.