React Hooks

React Hooks revolutionized the way developers build React applications by allowing functional components to manage state and side effects. This guide explores advanced hooks and their practical applications.

Understanding Hooks

Hooks enable function components to use React features without writing a class. Advanced hooks help in optimizing performance, managing complex state logic, and handling side effects efficiently.

1. useReducer: Managing Complex State

useReducer is an alternative to useState for managing complex state logic.

Example:

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 };
    default:
      throw new Error();
  }
}

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

2. useContext: Managing Global State

useContext allows sharing state between components without prop drilling.

Example:

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

const ThemeContext = createContext();

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

function ThemedComponent() {
  const { theme, setTheme } = useContext(ThemeContext);
  return (
    <div>
      <p>Current Theme: {theme}</p>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
    </div>
  );
}

3. useMemo: Performance Optimization

useMemo memoizes expensive calculations.

Example:

import { useMemo, useState } from 'react';

function ExpensiveCalculation({ num }) {
  const squared = useMemo(() => {
    console.log('Calculating Square');
    return num * num;
  }, [num]);

  return <p>Squared Value: {squared}</p>;
}

4. useCallback: Optimizing Function References

useCallback memoizes function instances.

Example:

import { useCallback, useState } from 'react';

function Button({ onClick }) {
  return <button onClick={onClick}>Click Me</button>;
}

function ParentComponent() {
  const [count, setCount] = useState(0);
  const increment = useCallback(() => setCount(count + 1), [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <Button onClick={increment} />
    </div>
  );
}

5. useRef: Handling DOM and Persistent Values

useRef stores values without causing re-renders.

Example:

import { useRef } from 'react';

function InputFocus() {
  const inputRef = useRef(null);

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={() => inputRef.current.focus()}>Focus Input</button>
    </div>
  );
}

6. useLayoutEffect: Synchronous Effects

useLayoutEffect runs synchronously after DOM mutations.

Example:

import { useLayoutEffect, useRef } from 'react';

function LayoutEffectComponent() {
  const divRef = useRef(null);

  useLayoutEffect(() => {
    console.log(divRef.current.offsetWidth);
  });

  return <div ref={divRef}>Hello</div>;
}