Engineer's Tutorial
Aug 15, 2024 โ‹… 6 min read
React Hooks, Effects and Equivalent lifecycle methods
NY

Nishi Yadav

Mern stack developer

Table of contents
Hello , Fellow Developers! ๐Ÿ‘‹

Welcome to Engineer's Tutorial! ๐ŸŽ‰ I'm thrilled to have you here. Whether you're just starting out or looking to sharpen your skills, you're in the right place. Let's dive into the world of React Hooks together! ๐Ÿ’ปโœจ

 

What Are React Hooks?

React Hooks are built-in functions that let you use state and other features in your functional components. Before Hooks, you needed to write class components to do this, but now you can use simple functions, making your code easier to write and understand.

 

Why Should You Use Hooks?
  • Simplify Your Code: Hooks let you add state and side effects to your components without writing a lot of code.
  • Reuse Logic Easily: With custom hooks, you can extract reusable logic from your components and share it across your app.
  • No More Classes: You don't need to worry about confusing this keyword or lifecycle methods in class components.

 

Table of Hooks and Use Cases:
Hooks
Use Cases
useState

Managing component state, like form inputs or toggling UI elements.

useEffect

Handling side effects, like data fetching or subscriptions. 

useContext

Accessing global data without prop drilling.  

useReducer

 Managing complex state logic, like multiple actions on a counter. 

useCallback

Memoizing functions to prevent unnecessary re-renders. 

useMemo

Optimizing performance by memoizing computed values.

useRef

Accessing DOM elements directly, storing mutable values without re-renders.

useLayoutEffect

Running effects after DOM updates but before painting on screen.

useImperativeHandle

Customizing instance values when using `ref` in parent components.

useDebugValue

Adding custom labels to display in React DevTools.


 

Getting Started with Basic Hooks

Let's understand detailed Description of Each Hook with Example:
useState

Description: useState is used to declare state variables in functional components.

Example:

import React, { useState } from 'react';
   function Counter() {
       const [count, setCount] = useState(0);
       return (
           <div>
               <p>You clicked {count} times</p>
               <button onClick={() => setCount(count + 1)}>Click me</button>
           </div>
       );
   }

 

useEffect

Description: useEffect handles side effects like fetching data, directly replacing lifecycle methods such as componentDidMount.

Example:

   import React, { useState, useEffect } from 'react';
   function DataFetcher() {
       const [data, setData] = useState(null);
       useEffect(() => {
           fetch('https://api.example.com/data')
               .then(response => response.json())
               .then(data => setData(data));
       }, []); // Run once after the first render
       return <div>{data ? <pre>{JSON.stringify(data, null, 2)}</pre> : "Loading..."}</div>;
   }

 

useContext

Description: useContext allows you to use values from a context without manually passing props through every component.

Example:

 import React, { useContext } from 'react';
   const ThemeContext = React.createContext();
   function ThemeButton() {
       const theme = useContext(ThemeContext);
       return <button style={{ background: theme }}>Click me</button>;
   }

 

useReducer

Description: `useReducer` is useful for managing complex state logic, similar to `redux`.

Example:

import React, { 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:
               return state;
       }
   }
   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>
           </div>
       );
   }
   

 

useCallback

Description: useCallback is used to memoize functions so that they donโ€™t get re-created on every render, which can be important for performance optimization.

Example:

import React, { useState, useCallback } from 'react';
function Button({ handleClick, children }) {
   return <button onClick={handleClick}>{children}</button>;
}
function Counter() {
   const [count, setCount] = useState(0);
   const increment = useCallback(() => {
       setCount((prevCount) => prevCount + 1);
   }, []);
   return (
       <div>
           <p>Count: {count}</p>
           <Button handleClick={increment}>Increment</Button>
       </div>
   );
}
export default Counter;

 

useMemo

Description: useMemo is used to memoize expensive calculations so that they are only recalculated when necessary.

Example:

import React, { useState, useMemo } from 'react';
function ExpensiveCalculation({ num }) {
   const computeFactorial = (n) => {
       if (n === 0) return 1;
       return n * computeFactorial(n - 1);
   };
   const factorial = useMemo(() => computeFactorial(num), [num]);
   return <p>Factorial of {num} is {factorial}</p>;
}
function App() {
   const [number, setNumber] = useState(5);
   return (
       <div>
           <ExpensiveCalculation num={number} />
           <button onClick={() => setNumber(number + 1)}>Increment Number</button>
       </div>
   );
}
export default App;

 
useRef

Description: useRef is used to access DOM elements directly or to persist values across renders without causing a re-render.

Example:

import React, { useRef } from 'react';
function TextInput() {
   const inputRef = useRef(null);
   const focusInput = () => {
       inputRef.current.focus();
   };
   return (
       <div>
           <input ref={inputRef} type="text" />
           <button onClick={focusInput}>Focus Input</button>
       </div>
   );
}
export default TextInput;

 

useLayoutEffect

Description: useLayoutEffect is similar to useEffect, but it runs synchronously after all DOM mutations and before the browser has a chance to paint, making it ideal for measurements or adjustments to the DOM.

Example:

import React, { useState, useLayoutEffect, useRef } from 'react';
function MeasureComponent() {
   const [height, setHeight] = useState(0);
   const divRef = useRef();
   useLayoutEffect(() => {
       setHeight(divRef.current.getBoundingClientRect().height);
   }, []);
   return (
       <div ref={divRef} style={{ padding: '20px', backgroundColor: 'lightblue' }}>
           <p>The height of this div is {height}px.</p>
       </div>
   );
}
export default MeasureComponent;

 

useImperativeHandle

Description: useImperativeHandle customizes the instance value that is exposed when using ref in parent components, allowing you to control what is accessible to the parent.

Example:

import React, { useRef, useImperativeHandle, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
   const inputRef = useRef();
   useImperativeHandle(ref, () => ({
       focus: () => {
           inputRef.current.focus();
       },
       scrollToTop: () => {
           window.scrollTo(0, 0);
       }
   }));
   return <input ref={inputRef} type="text" />;
});
function ParentComponent() {
   const fancyInputRef = useRef();
   return (
       <div>
           <FancyInput ref={fancyInputRef} />
           <button onClick={() => fancyInputRef.current.focus()}>Focus Input</button>
           <button onClick={() => fancyInputRef.current.scrollToTop()}>Scroll to Top</button>
       </div>
   );
}
export default ParentComponent;

 

useDebugValue

Description: useDebugValue is used to display a label in React DevTools for custom hooks, aiding in debugging.

Example:

import React, { useState, useDebugValue } from 'react';
function useFriendStatus(friendID) {
   const [isOnline, setIsOnline] = useState(false);
   useDebugValue(isOnline ? 'Online' : 'Offline');
   // Simulate a subscription to a friend's status
   useEffect(() => {
       const handleStatusChange = (status) => {
           setIsOnline(status.isOnline);
       };
       // Fake API call to subscribe to friend's status
       fakeAPI.subscribe(friendID, handleStatusChange);
       return () => {
           fakeAPI.unsubscribe(friendID, handleStatusChange);
       };
   }, [friendID]);
   return isOnline;
}
function FriendStatus({ friendID }) {
   const isOnline = useFriendStatus(friendID);
   return <div>{isOnline ? 'Online' : 'Offline'}</div>;
}
export default FriendStatus;

 
Achieving Different Lifecycle Methods Using Hooks:

Lifecycle Method

Equivalent Hook

componentDidMount

useEffect(() => {...}, [])

componentDidUpdate

useEffect(() => {...}, [dependencies])

componentWillUnmount

useEffect(() => { return () => {...}; }, [])

getDerivedStateFromProps

Combined use of useState and useEffect

shouldComponentUpdate

Optimized using React.memo and useCallback

componentWillMount

Not directly supported, handled with other techniques


 

Creating Your Own Hooks

Custom hooks are a great way to reuse logic across your components. If you find yourself writing the same code in multiple places, you can create a custom hook to make your code more DRY (Don't Repeat Yourself).

 useFetch - Custom Hook should start with "use"

Example:

import { useState, useEffect } from 'react';
function useFetch(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    useEffect(() => {
        fetch(url)
            .then(response => response.json())
            .then(data => {
                setData(data);
                setLoading(false);
            });
    }, [url]);
    return { data, loading };
}

 

Do's, Don'ts, and Points to Remember:

Do's:

Don'ts:

Use hooks in functional components only.Donโ€™t use hooks inside loops, conditions, or nested functions.
 Keep your components small and focused.Avoid overusing hooks if they donโ€™t simplify your code.
Always include dependencies in useEffect when needed.Donโ€™t forget to clean up effects in useEffect when necessary.

 

Ref: React official website, Mdn web docs


 

Happy coding! ๐ŸŽ‰๐ŸŽ‰๐ŸŽ‰