SalakCode SalakCode
React Internals

React Hooks

Use state and lifecycle features in functional components

Intermediate
react hooks state functional-components

Definition

React Hooks are functions that let you use state and other React features in functional components. Introduced in React 16.8, they enable you to reuse stateful logic without changing your component hierarchy and make components more composable.

Core Hooks

useState

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(prev => prev - 1)}>
        Decrement (functional update)
      </button>
    </div>
  );
}

useEffect

import { useState, useEffect } from 'react';

function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  
  useEffect(() => {
    // Runs after render
    let cancelled = false;
    
    async function fetchUser() {
      setLoading(true);
      const response = await fetch(`/api/users/${userId}`);
      const data = await response.json();
      
      if (!cancelled) {
        setUser(data);
        setLoading(false);
      }
    }
    
    fetchUser();
    
    // Cleanup function
    return () => {
      cancelled = true;
    };
  }, [userId]); // Dependency array
  
  if (loading) return <div>Loading...</div>;
  return <div>{user.name}</div>;
}

useContext

import { createContext, useContext, useState } from 'react';

const ThemeContext = createContext(null);

function ThemeProvider({ children }) {
  const [theme, setTheme] = useState('light');
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemedButton() {
  const { theme, setTheme } = useContext(ThemeContext);
  
  return (
    <button 
      onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
      className={theme}
    >
      Current theme: {theme}
    </button>
  );
}

Additional Hooks

useRef

import { useRef, useEffect } from 'react';

function TextInput() {
  const inputRef = useRef(null);
  
  useEffect(() => {
    // Focus input on mount
    inputRef.current.focus();
  }, []);
  
  // Store previous value
  const prevCountRef = useRef();
  useEffect(() => {
    prevCountRef.current = count;
  });
  
  return <input ref={inputRef} />;
}

useMemo and useCallback

import { useMemo, useCallback } from 'react';

function ExpensiveComponent({ data, onUpdate }) {
  // Memoize expensive computation
  const processedData = useMemo(() => {
    return data.map(item => expensiveOperation(item));
  }, [data]);
  
  // Memoize callback
  const handleClick = useCallback((id) => {
    onUpdate(id);
  }, [onUpdate]);
  
  return (
    <List 
      items={processedData} 
      onItemClick={handleClick} 
    />
  );
}

useReducer

import { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    case 'reset':
      return initialState;
    default:
      throw new Error('Unknown action');
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    <div>
      Count: {state.count}
      <button onClick={() => dispatch({ type: 'increment' })}>+</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
      <button onClick={() => dispatch({ type: 'reset' })}>Reset</button>
    </div>
  );
}

Custom Hooks

useLocalStorage

function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (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.error(error);
    }
  };
  
  return [storedValue, setValue];
}

// Usage
const [name, setName] = useLocalStorage('name', 'Guest');

useFetch

function useFetch(url) {
  const [state, setState] = useState({
    data: null,
    loading: true,
    error: null
  });
  
  useEffect(() => {
    let cancelled = false;
    
    async function fetchData() {
      try {
        setState(prev => ({ ...prev, loading: true }));
        const response = await fetch(url);
        const data = await response.json();
        
        if (!cancelled) {
          setState({ data, loading: false, error: null });
        }
      } catch (error) {
        if (!cancelled) {
          setState({ data: null, loading: false, error });
        }
      }
    }
    
    fetchData();
    
    return () => {
      cancelled = true;
    };
  }, [url]);
  
  return state;
}

Rules of Hooks

  1. Only call hooks at the top level - not in loops, conditions, or nested functions
  2. Only call hooks from React functions - components or custom hooks
  3. Ensure exhaustive dependencies in useEffect
// Wrong
function BadComponent() {
  if (condition) {
    useEffect(() => {}); // Conditional hook!
  }
}

// Right
function GoodComponent() {
  useEffect(() => {
    if (condition) {
      // Logic inside effect
    }
  }, []);
}
Key Takeaway

React Hooks revolutionized functional components by adding state and lifecycle capabilities. Master useState, useEffect, and useContext first, then move to optimization hooks like useMemo and useCallback. Create custom hooks to extract and reuse component logic. Always follow the Rules of Hooks to avoid subtle bugs.

Resources

Related Topics