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.

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;

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>
  );
}

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>;
}

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>
  );
}

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>
  );
}

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>;
}