React LogoCreating Custom Hooks

Custom Hooks are a powerful feature in React that allows you to extract and reuse stateful logic from components. They are functions that let you 'hook into' React features like state and lifecycle methods (via `useState`, `useEffect`, etc.) without writing a class component.

What are Custom Hooks?
At their core, a custom hook is a JavaScript function whose name starts with `use` (e.g., `useLogger`, `useToggle`, `useFetchData`). This naming convention is crucial as it signals to React that the function follows the rules of hooks and might contain calls to other built-in hooks.

Why Use Custom Hooks?
1. Logic Reusability: The primary benefit. If you find yourself writing the same logic (e.g., managing a toggle state, fetching data, interacting with browser storage) in multiple components, a custom hook can encapsulate that logic and allow you to reuse it across your application.
2. Separation of Concerns: Custom hooks help you separate concerns within your components. Components can focus purely on rendering UI, while the complex state management or side effects are delegated to a hook.
3. Cleaner Components: By abstracting logic into hooks, your functional components become much shorter, more readable, and easier to understand.
4. Improved Testability: Logic within custom hooks can often be tested more in isolation than if it were tightly coupled within a component.

How to Create a Custom Hook:
1. Naming: Always start the function name with `use` (e.g., `useLocalStorage`, `usePrevious`).
2. Encapsulation: Inside the custom hook, you can call other built-in React hooks (`useState`, `useEffect`, `useContext`, `useRef`, etc.) and any other JavaScript logic.
3. Return Values: A custom hook can return anything: an object, an array, a primitive value, or nothing. It typically returns the state and any functions needed to manipulate that state, making it similar to `useState` which returns `[value, setValue]`.
4. No JSX: Custom hooks are not React components. They do not return JSX. Their sole purpose is to provide reusable logic.

Rules of Hooks (Apply to Custom Hooks Too):
When creating and using custom hooks, remember the two golden rules of hooks:
1. Call Hooks Only at the Top Level: Don't call hooks inside loops, conditions, or nested functions. Hooks must always be called in the same order on every render.
2. Call Hooks Only from React Functions: Call them from React function components or other custom hooks, but not from regular JavaScript functions.

Example Code

// --- src/hooks/useLocalStorage.js ---
import { useState, useEffect } from 'react';

function useLocalStorage(key, initialValue) {
  // State to store our value
  // Pass initial state function to useState so logic is only executed once
  const [storedValue, setStoredValue] = useState(() => {
    try {
      // Check if window is defined to handle SSR scenarios
      if (typeof window === 'undefined') {
        return initialValue;
      }
      // Get from local storage by key
      const item = window.localStorage.getItem(key);
      // Parse stored json or if none return initialValue
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      // If error also return initialValue
      console.error(`Error reading localStorage key "${key}":`, error);
      return initialValue;
    }
  });

  // useEffect to update local storage when the state changes
  useEffect(() => {
    try {
      if (typeof window !== 'undefined') {
        // Allow storedValue to be a function so we can use it like useState's setter
        const valueToStore =
          typeof storedValue === 'function' ? storedValue(storedValue) : storedValue;
        window.localStorage.setItem(key, JSON.stringify(valueToStore));
      }
    } catch (error) {
      // A more advanced implementation would handle the error case
      console.error(`Error writing to localStorage key "${key}":`, error);
    }
  }, [key, storedValue]); // Only re-run if key or storedValue changes

  return [storedValue, setStoredValue];
}

export default useLocalStorage;

// --- src/App.js ---
import React from 'react';
import useLocalStorage from './hooks/useLocalStorage'; // Adjust path if necessary

function App() {
  // Use our custom hook to persist the 'name' and 'age' state in localStorage
  const [name, setName] = useLocalStorage('myAppName', 'React User');
  const [age, setAge] = useLocalStorage('myAppAge', 25);

  const handleNameChange = (event) => {
    setName(event.target.value);
  };

  const handleAgeChange = (event) => {
    setAge(Number(event.target.value));
  };

  return (
    <div>
      <h1>User Profile</h1>
      <div>
        <label>
          Name:
          <input type="text" value={name} onChange={handleNameChange} />
        </label>
      </div>
      <div>
        <label>
          Age:
          <input type="number" value={age} onChange={handleAgeChange} />
        </label>
      </div>
      <p>Hello, {name}! You are {age} years old.</p>
      <p>
        <em>
          Try refreshing the page or closing and reopening your browser.
          Your name and age will be remembered because they are stored in localStorage
          using the custom hook!
        </em>
      </p>
    </div>
  );
}

export default App;