Benefits of React Hooks
Recently I have started working with React hooks and have really enjoyed the experience. This post outlines three of the benefits I have observed.
Improving Readability of Component Tree
The useContext
hook has been a blessing for greatly improving the readability of JSX. It allows context values to be read outside of JSX. This was also previously possible in class components by using the static contextType
property but is even cleaner with useContext
.
Aside from the code being easier to read it is also much easier to read the component tree in the React dev tools when debugging. The value of this really adds up for components which previously used multiple nested contexts.
function MyComponent() {
return (
<MyContext.Consumer>
{(handleClick) => (
<button onClick={handleClick} />
)}
</MyContext.Consumer>
);
}
function MyComponent() {
const handleClick = useContext(MyContext);
return <button onClick={handleClick} />;
}
Encapsulating Side effects
With class components the setup and teardown of side effects was split across multiple lifecycle methods. For example, event listeners could be added in componentDidMount
and later removed in componentWillUnmount
. If a component has multiple side effects this could lead to less readable code with related logic split across several incohesive lifecycle methods.
useEffect
solves this problem by handling both the setup and teardown of side effects. It does so by allowing the effect function to return a function to teardown the effect.useEffect(() => {
// setup a side effect
window.addEventListener("resize", resizeHandler);
// remove the side effect
return () => window.removeEventListener("resize", resizeHandler);
}, []);
Passing a dependency list as the second argument also neatly negates the need to check if dependencies have changed in componentDidUpdate
.
Composable, Reusable Logic
Custom hooks are a great mechanism for sharing logic across comoponents. A custom hook is simply a function which uses one or more React hooks. It can be called within a functional component, just like a standard hook.
A nice example is a custom hook for tracking the result of a media query and returning this state to the component. It illustrates how hooks can be combined. It uses useEffect
to set up an event listener to track changes in whether a media query is matched or not. It also uses useState
to store this state and return the result to the calling component.
function useMatchMedia(mediaQuery) {
const [matches, setMatches] = useState(window.matchMedia(mediaQuery).matches);
useEffect(() => {
const updateMatches = event => {
setMatches(event.matches);
};
const mediaQueryList = window.matchMedia(mediaQuery);
setMatches(mediaQueryList.matches);
mediaQueryList.addListener(updateMatches);
return () => mediaQueryList.removeListener(updateMatches);
}, [mediaQuery]);
return matches;
}
Components can then simply subscribe to updates on a media query's status without the need to explicitly manage either the side effect or state. This can be especially useful when it is necessary to interact with components whose props you need to base upon viewport dimensions.
function ResponsiveSideBar() {
const desktop = useMatchMedia("(min-width: 720px)");
return <Sidebar direction={desktop ? "left" : "bottom"} />;
}
Conclusion
All in all I am really enjoying React hooks and the benefits they are bringing for making my code much more readable and maintainable. Excited to get stuck in with some more hooks in the future!