Custom Hooks

How to extract and share stateful logic between components without sharing the state itself.

React4 min readConcept 23 of 46

What is a Custom Hook?

A custom Hook is just a regular JavaScript function whose name starts with use (like useWindowSize or useFetch).

Unlike normal helper functions, a custom Hook is allowed to call other React Hooks (like useState and useEffect) inside it.

Reusing Stateful Logic

If you build a component that tracks the window's width (using useState and a resize event listener in useEffect), and later you need that exact same logic in a completely different component, you shouldn't copy-paste the code.

Instead, you extract that logic into a useWindowSize hook. Now both components can just call const width = useWindowSize();.

How to build one

**1. Identify duplicated logic:** Look for useState and useEffect patterns repeated across components.

**2. Extract it:** Create a function named useSomething and move the logic inside it.

**3. Return the data:** Return the state variable (or anything else the components need) from your hook.

**4. Use it:** Call your new hook from your components just like a built-in React hook.

Logic Extraction

Click 'Extract Logic' to watch how duplicated code in two separate components is pulled out and merged into a single reusable custom hook, leaving behind clean components.

useOnlineStatus.js
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true); useEffect(() => { const setOnline = () => setIsOnline(true); const setOffline = () => setIsOnline(false); window.addEventListener('online', setOnline); window.addEventListener('offline', setOffline); return () => { window.removeEventListener('online', setOnline); window.removeEventListener('offline', setOffline); }; }, []);
  return isOnline;
}
Chat.jsx
function Chat() {
const [isOnline, setIsOnline] = useState(true); useEffect(() => { const setOnline = () => setIsOnline(true); const setOffline = () => setIsOnline(false); window.addEventListener('online', setOnline); window.addEventListener('offline', setOffline); return () => { window.removeEventListener('online', setOnline); window.removeEventListener('offline', setOffline); }; }, []);

  return (
    <div className={isOnline ? 'green' : 'red'}>
      Chat Status
    </div>
  );
}
Profile.jsx
function Profile() {
const [isOnline, setIsOnline] = useState(true); useEffect(() => { const setOnline = () => setIsOnline(true); const setOffline = () => setIsOnline(false); window.addEventListener('online', setOnline); window.addEventListener('offline', setOffline); return () => { window.removeEventListener('online', setOnline); window.removeEventListener('offline', setOffline); }; }, []);

  return (
    <div>
      Profile {isOnline ? '🟢' : '🔴'}
    </div>
  );
}

Notice how the identical state and effect logic is completely removed from both components and encapsulated in a single, reusable function.

Check yourself

Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.

  1. 1If Component A and Component B both call `const count = useCounter()`, what happens to the state?
  2. 2Why must a custom hook's name start with 'use'?
  3. 3When should you create a custom hook?

Remember this

  • Custom hooks extract stateful logic to prevent code duplication.
  • Their names MUST start with 'use'.
  • They are allowed to call other hooks (like useState and useEffect).
  • They share LOGIC, not STATE. Every call creates isolated state.

Done with this concept?

Mark it complete to track your progress. No login needed.