DEV Community

SOVANNARO
SOVANNARO

Posted on

1

All React Hooks Explained with TypeScript Examples

React Hooks revolutionized the way developers write functional components by allowing them to use state, lifecycle methods, and other React features without writing a class. Since their introduction in React 16.8, hooks have become an essential part of modern React development. In this article, we will explore all the built-in React hooks, explain their purpose, and provide TypeScript examples to demonstrate their usage.


1. useState

The useState hook is the most commonly used hook in React. It allows functional components to manage state.

Syntax:

const [state, setState] = useState<Type>(initialState);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useState } from 'react';

const Counter: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useState takes an initial state value and returns an array with two elements: the current state and a function to update it.
  • In the example, count is the state variable, and setCount is the function to update it.

2. useEffect

The useEffect hook is used to perform side effects in functional components, such as fetching data, subscribing to events, or manually changing the DOM.

Syntax:

useEffect(() => {
  // Side effect logic
  return () => {
    // Cleanup logic
  };
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useState, useEffect } from 'react';

const Timer: React.FC = () => {
  const [seconds, setSeconds] = useState<number>(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds((prev) => prev + 1);
    }, 1000);

    return () => clearInterval(interval); // Cleanup on unmount
  }, []);

  return <p>Seconds: {seconds}</p>;
};

export default Timer;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • The useEffect hook runs after every render by default.
  • The second argument is an array of dependencies. If any dependency changes, the effect runs again.
  • If you return a function from useEffect, it will be used for cleanup (e.g., clearing timers or unsubscribing from events).

3. useContext

The useContext hook allows you to access the value of a React context without wrapping your component in a Context.Consumer.

Syntax:

const value = useContext<ContextType>(MyContext);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useContext } from 'react';

const ThemeContext = React.createContext<string>('light');

const ThemedComponent: React.FC = () => {
  const theme = useContext(ThemeContext);

  return <p>Current Theme: {theme}</p>;
};

export default ThemedComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useContext takes a context object and returns its current value.
  • The value is determined by the nearest Context.Provider above the component in the tree.

4. useReducer

The useReducer hook is an alternative to useState for managing complex state logic. It is inspired by Redux and uses a reducer function to handle state transitions.

Syntax:

const [state, dispatch] = useReducer<Reducer<State, Action>>(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useReducer } from 'react';

type State = { count: number };
type Action = { type: 'increment' } | { type: 'decrement' };

const reducer = (state: State, action: Action): State => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error('Unknown action type');
  }
};

const Counter: React.FC = () => {
  const [state, dispatch] = useReducer(reducer, { count: 0 });

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>Increment</button>
      <button onClick={() => dispatch({ type: 'decrement' })}>Decrement</button>
    </div>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useReducer takes a reducer function and an initial state, and returns the current state and a dispatch function to trigger actions.
  • The reducer function determines how the state should be updated based on the action type.

5. useRef

The useRef hook is used to create a mutable reference that persists across renders. It is commonly used to access DOM elements or store mutable values without triggering re-renders.

Syntax:

const ref = useRef<Type>(initialValue);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useRef, useEffect } from 'react';

const FocusInput: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return <input ref={inputRef} type="text" />;
};

export default FocusInput;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useRef returns a mutable object with a current property.
  • Changes to ref.current do not trigger re-renders.

6. useMemo

The useMemo hook is used to memoize expensive calculations so that they are only recomputed when their dependencies change.

Syntax:

const memoizedValue = useMemo<Type>(() => computeExpensiveValue(a, b), [a, b]);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useMemo, useState } from 'react';

const ExpensiveComponent: React.FC = () => {
  const [count, setCount] = useState<number>(0);
  const [input, setInput] = useState<string>('');

  const squaredValue = useMemo(() => {
    console.log('Calculating squared value...');
    return count * count;
  }, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Squared Value: {squaredValue}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
    </div>
  );
};

export default ExpensiveComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useMemo takes a function and an array of dependencies. It returns the memoized value, which is only recalculated when the dependencies change.

7. useCallback

The useCallback hook is used to memoize functions so that they are only recreated when their dependencies change. This is useful for optimizing performance in child components that rely on reference equality.

Syntax:

const memoizedCallback = useCallback<Function>(() => {
  // Function logic
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useState, useCallback } from 'react';

const ChildComponent: React.FC<{ onClick: () => void }> = ({ onClick }) => {
  console.log('ChildComponent rendered');
  return <button onClick={onClick}>Click Me</button>;
};

const ParentComponent: React.FC = () => {
  const [count, setCount] = useState<number>(0);

  const handleClick = useCallback(() => {
    setCount((prev) => prev + 1);
  }, []);

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

export default ParentComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useCallback returns a memoized version of the function that only changes if one of the dependencies has changed.

8. useLayoutEffect

The useLayoutEffect hook is similar to useEffect, but it runs synchronously after all DOM mutations. Use this hook when you need to read layout from the DOM and re-render synchronously.

Syntax:

useLayoutEffect(() => {
  // Synchronous logic
}, [dependencies]);
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useLayoutEffect, useRef } from 'react';

const MeasureElement: React.FC = () => {
  const ref = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    if (ref.current) {
      console.log('Element width:', ref.current.offsetWidth);
    }
  }, []);

  return <div ref={ref}>Measure Me</div>;
};

export default MeasureElement;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useLayoutEffect is useful for DOM measurements or when you need to perform actions that depend on the DOM layout.

9. useImperativeHandle

The useImperativeHandle hook customizes the instance value that is exposed when using ref with forwardRef.

Syntax:

useImperativeHandle(ref, () => ({
  // Custom instance methods
}));
Enter fullscreen mode Exit fullscreen mode

Example:

import React, { useRef, useImperativeHandle, forwardRef } from 'react';

type InputRef = {
  focus: () => void;
};

const CustomInput = forwardRef<InputRef>((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  useImperativeHandle(ref, () => ({
    focus: () => {
      if (inputRef.current) {
        inputRef.current.focus();
      }
    },
  }));

  return <input ref={inputRef} type="text" />;
});

const ParentComponent: React.FC = () => {
  const inputRef = useRef<InputRef>(null);

  return (
    <div>
      <CustomInput ref={inputRef} />
      <button onClick={() => inputRef.current?.focus()}>Focus Input</button>
    </div>
  );
};

export default ParentComponent;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useImperativeHandle is used to expose specific methods or properties to parent components.

10. useDebugValue

The useDebugValue hook is used to display a label for custom hooks in React DevTools.

Syntax:

useDebugValue(value);
Enter fullscreen mode Exit fullscreen mode

Example:

import { useState, useDebugValue } from 'react';

const useCustomHook = () => {
  const [value] = useState<string>('Hello');
  useDebugValue(value);
  return value;
};

const DebugValueExample: React.FC = () => {
  const value = useCustomHook();
  return <p>{value}</p>;
};

export default DebugValueExample;
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • useDebugValue is primarily used for debugging custom hooks.

Conclusion

React hooks provide a powerful and flexible way to manage state, side effects, and other features in functional components. By understanding and using these hooks effectively, you can write cleaner, more maintainable, and performant React applications. With TypeScript, you can add type safety to your hooks, making your code more robust and easier to debug.

Whether you're managing state with useState, handling side effects with useEffect, or optimizing performance with useMemo and useCallback, hooks are an essential tool in every React developer's toolkit. Happy coding!

Billboard image

The fastest way to detect downtimes

Join Vercel, CrowdStrike, and thousands of other teams that trust Checkly to streamline monitoring.

Get started now

Top comments (0)

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

👋 Kindness is contagious

Immerse yourself in a wealth of knowledge with this piece, supported by the inclusive DEV Community—every developer, no matter where they are in their journey, is invited to contribute to our collective wisdom.

A simple “thank you” goes a long way—express your gratitude below in the comments!

Gathering insights enriches our journey on DEV and fortifies our community ties. Did you find this article valuable? Taking a moment to thank the author can have a significant impact.

Okay