React Hooks are functions that allow you to use state and life-cycle features from function components. If you’ve been using React, you’ve probably used their built-in Hooks, such as useState, useEffect, and useContext. However, React does not limit you to using only those Hooks.
Creating your own Hooks in React can be useful in various situations. Custom Hooks are an excellent way to share logic between components and improve your component tree’s readability.
This article explains how to implement various custom Hooks in React. Let’s get started.
Implementing a custom Hook
A custom Hook is a JavaScript function that begins with use. It is not mandatory to begin the custom Hook name with “use,” but without it, React would be unable to check for violations of the Hooks rules automatically. Therefore, it is critical to adhere to that naming convention.
Consider the following example of defining a custom Hook called useFriendStatus.
import { useState, useEffect } from 'react'; function useFriendStatus(friendID) { const [isOnline, setIsOnline] = useState(null); useEffect(() => { function handleStatusChange(status) { setIsOnline(status.isOnline); } ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange); return () => { ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange); }; }); return isOnline; }
In the above example, we call the useState and useEffect Hooks unconditionally at the top level of our custom Hook, just as we would in a component. The useFriendStatus Hook’s purpose is to subscribe us to a friend’s status. As a result, it accepts a friendID as an argument and returns whether the friend is online.
Syncfusion React UI components are the developers’ choice to build user-friendly web applications. You deserve them too.
If we didn’t define the useFriendStatus custom Hook, we’d have to write the logic inside this Hook in every component to subscribe to a friend’s status. So, instead of duplicating code, we can use this custom Hook.
Now, let’s look at how we can put this custom Hook to use.
function FriendStatus(props) { const isOnline = useFriendStatus(props.friend.id); if (isOnline === null) { return 'Loading...'; } return isOnline ? 'Online' : 'Offline'; } export default FriendStatus; function FriendListItem(props) { const isOnline = useFriendStatus(props.friend.id); return ( <li style={{ color: isOnline ? 'green' : 'black' }}> {props.friend.name} </li> ); } export default FriendListItem;
In the above example, you can see that the useFriendStatus Hook is used in two different functional components, FriendStatus and FriendListItem. You can even use the same custom Hook twice within the same component.
Note: The state is not shared between two components that use the same Hook.
Passing information between Hooks
Since Hooks are a type of function, we can pass data between them. Consider the following example where a user can select any friend and see whether that friend is online.
const friendList = [ { id: 1, name: 'John' }, { id: 2, name: 'Michael' }, { id: 3, name: 'Mary' }, ]; function ChatRecipientPicker() { const [recipientID, setRecipientID] = useState(1); const isRecipientOnline = useFriendStatus(recipientID); return ( <> <Circle color={isRecipientOnline ? 'green' : 'red'} /> <select value={recipientID} onChange={e => setRecipientID(Number(e.target.value))} > {friendList.map(friend => ( <option key={friend.id} value={friend.id}> {friend.name} </option> ))} </select> </> ); }
Here, the recipientID state variable value is passed as an argument to the custom useFriendStatus Hook. When the user selects a different friend in the <select> picker, we use the useState Hook to update the recipientID. The useFriendStatus Hook then unsubscribes from the previously selected friend and subscribes to the newly selected friend’s status.
All Syncfusion’s 70+ React UI components are well-documented. Refer to them to get started quickly.
Custom Hooks examples
Now that you have an understanding of how custom Hooks work, let’s look at several scenarios for creating custom Hooks.
useLocalStorage
To use local storage in React, we can construct a custom Hook. The Hook allows you to preserve data in the browser as key-value pairs for later use.
import { useState } from "react"; export default function App() { // Usage const [name, setName] = useLocalStorage("name", "John"); return ( <div> <input type="text" placeholder="Enter your name" value={name} onChange={(e) => setName(e.target.value)} /> </div> ); } // Hook function useLocalStorage(key, initialValue) { const [storedValue, setStoredValue] = useState(() => { try { const item = window.localStorage.getItem(key); return item ? JSON.parse(item) : initialValue; } catch (error) { console.log(error); return initialValue; } }); const setValue = (value) => { try { const valueToStore = value instanceof Function ? value(storedValue) : value; setStoredValue(valueToStore); window.localStorage.setItem(key,JSON.stringify(valueToStore)); } catch (error) { console.log(error); } }; return [storedValue, setValue]; }
useToggle
This is a simple but helpful custom Hook that can be used effectively in many web applications. The useToggle Hook allows you to take a true or false value and convert it to the inverse. It can be used to toggle modals and menus.
import { useCallback, useState } from 'react'; export default function App() { // Usage const [isModalOpen, setIsModalOpen] = useToggle(); return ( <button onClick={setIsModalOpen}> {isModalOpen ? 'Close Modal' : 'Open Modal'} </button> ); } // Hook const useToggle = (initialState = false) => { const [state, setState] = useState(initialState); const toggle = useCallback(() => setState(state => !state), []); return [state, toggle] }
useEventListener
You may find yourself adding a lot of event listeners using useEffect in your code. In such situations, you should move that logic to a custom Hook. The useEventListener Hook first ensures that the element supports the addEventListener method, and then it creates and adds an event listener. If eventName or element changes, the Hook will rerun.
import { useState, useRef, useEffect, useCallback } from "react"; export default function App() { const [coords, setCoords] = useState({ x: 0, y: 0 }); const handler = useCallback( ({ clientX, clientY }) => { setCoords({ x: clientX, y: clientY }); }, [setCoords] ); // Usage useEventListener('mousemove', handler); return ( <h1> The mouse position is ({coords.x}, {coords.y}) </h1> ); } // Hook function useEventListener(eventName, handler, element = window) { const savedHandler = useRef(); useEffect(() => { savedHandler.current = handler; }, [handler]); useEffect( () => { const isSupported = element && element.addEventListener; if (!isSupported) return; const eventListener = event => savedHandler.current(event); element.addEventListener(eventName, eventListener); return () => { element.removeEventListener(eventName, eventListener); }; }, [eventName, element] ); }
Similarly, you can write custom Hooks to handle various use cases, such as form handling, animation, timers, and many others.
Be amazed exploring what kind of application you can develop using Syncfusion React components.
Advantages of custom Hooks
The main advantage of React Hooks is the reusability of stateful logic. In addition, custom Hooks can be easily shared with other components without changing the component hierarchy. Also, we don’t have to be concerned about the this keyword when using Hooks.
Compared to HOCs, the custom Hooks approach provides cleaner logic and a better understanding of the relationship between the data and the component. As a result, it’s easier to read and has fewer lines of code.
We can write separate unit tests for custom Hooks, making testing easier. It is also easier to test components with multiple custom Hooks using the React Testing Library. Explore the endless possibilities with Syncfusion’s outstanding React UI components.
Conclusion
Custom Hooks enable exceptional flexibility in sharing logic previously unavailable in React components. I hope this guide has given you a better understanding of creating your own React Hooks.
Thank you for reading.
The Syncfusion React suite offers over 80 high-performance, lightweight, modular, and responsive UI components in a single package. This library is the only suite you’ll ever need to construct a complete application.
If you have any questions or comments, you can contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!