React Hooks - A simple way to implement react features

React Hooks are a feature introduced in React 16.8 that allows developers to use state and other React features without writing a class. They provide a way to reuse stateful logic between components. Before Hooks, stateful logic could only be used in class components.

Hooks are functions that let you "hook into" React's state and lifecycle features from functional components. They allow you to manage component state, perform side effects, and tap into React's lifecycle methods such as mounting, updating, and unmounting.

There are several built-in Hooks provided by React, such as:

  • useState: This Hook allows you to add state to a functional component. It returns a pair of values: the current state and a function to update that state.
  • useEffect: This Hook allows you to perform side effects in a functional component. It replaces the lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount.
  • useContext: This Hook allows you to access the value of a Context in a functional component.
  • useReducer: This Hook is an alternative to useState and allows you to manage more complex state logic using a reducer function.
  • useCallback and useMemo: These Hooks optimize the performance of functional components by memoizing values and functions to avoid unnecessary re-renders.
  • useRef: This Hook allows you to create a mutable reference that persists across component renders.
  • useSelector: The useSelector hook in react-redux library simplifies the process of accessing and using data from the Redux store in your React components.
  • useDispatch: The useDispatch hook in react-redux library, allows you to access the dispatch function in your React components and trigger actions to update the Redux store.
  • useNavigate: The useNavigate hook in react router allows you to programmatically navigate between routes in your React Router v6 application.
  • useHistory: The useHistory hook in react router allows you to access the history object and have full control over the navigation and history management in your React Router application.

Hooks are not meant to replace class components entirely but provide an alternative way to write components with stateful logic using functional components. They promote reusability, better encapsulation, and easier testing.

UseState Hook

In React, the useState hook is used to add state to functional components. It allows you to declare and manage state variables within your components.

  1. Import the useState hook from the React library:

    import React, { useState } from 'react';
    
  2. Within your functional component, call the useState hook and provide an initial value for your state variable. It returns an array with two elements: the current state value and a function to update that state value.

    const MyComponent = () => {
      const [count, setCount] = useState(0);
    
      // ...
    };
  3. You can now use the state variable (count) and the corresponding updater function (setCount) within your component:

    const CounterComponent = () => {
      const [count, setCount] = useState(0);
    
      const incrementCount = () => {
        setCount(count + 1);
      };
    
      return (
        <div>
          <p>Count value is: {count}</p>
          <button onClick={incrementCount}>Increment</button>
        </div>
      );
    };

Note that when using useState, the state does not need to be an object; it can be a primitive value (like a number or string) or even an array.By using the useState hook, functional components can now have local state, making it easier to manage and update state within React applications.

useEffect Hook

In React, the useEffect hook is used to perform side effects in functional components. It allows you to run code in response to component lifecycle events, such as component mounting, updating, or unmounting. Here's how you can use the useEffect hook:

  1. Import the useEffect hook from the React library:

    import React, { useEffect } from 'react';
    
  2. Within your functional component, call the useEffect hook and provide a callback function as the first argument. This callback function will be executed after the component renders for the first time, and it can also run on subsequent updates.

    const MyComponent = () => {
      useEffect(() => {
        // Side effect code here
        // ...
    
        // Clean-up function (optional)
        return () => {
          // Clean-up code here
          // ...
        };
      }, []);
    
      // ...
    };

    In the example above, we define the useEffect hook within the MyComponent functional component. The callback function passed to useEffect will be executed after the initial render. Optionally, you can also provide a dependency array as the second argument to specify when the effect should run. An empty dependency array ([]) indicates that the effect should only run once, on component mounting.

  3. Within the callback function, you can perform various side effects, such as fetching data from an API, subscribing to event listeners, manipulating the DOM, or updating external libraries. The code inside the useEffect callback will run after the component renders.

    const ListComponent = () => {
      useEffect(() => {
        // Fetch items from an API endpoint
        const fetchData = async () => {
          const response = await fetch('https://api.mydata.com/items');
          const data = await response.json();
          // Handle the data here
        };
    
        fetchData();
      }, []);
    
      // ...
    };

    In the above example above, the fetchData function is called within the useEffect callback to fetch data asynchronously. By using the useEffect hook, you can manage side effects within functional components, keeping your code organized and avoiding the need for class components. It provides a way to incorporate asynchronous operations, subscriptions, and other side effects into your React applications.

useRef Hook

The useRef hook is used to create a mutable reference that persists across component renders. It allows you to access and manipulate DOM elements or values that need to be preserved between renders without triggering a re-render.

  1. Import the useRef hook from the React library:

    import React, { useRef } from 'react';
  2. In the component function, call the useRef hook and assign the returned value to a variable. This variable can hold a reference to a DOM element or any other value.

    const DataComponent = () => {
      const elRef = useRef();
    
      // ...
    };
  3. Assign the reference to a DOM element by passing the ref prop to the element you want to reference:

    const DataComponent = () => {
      const elRef = useRef();
    
      return (
        <div ref={elRef}>
          {/* Element content */}
        </div>
      );
    };
  4. You can access and manipulate the referenced element or value using the current property of the reference:

    const DataComponent = () => {
      const elRef = useRef();
    
      const handleClick = () => {
        // Access the referenced element
        console.log(elRef.current);
    
        // Manipulate the referenced element
        elRef.current.style.backgroundColor = 'red';
      };
    
      return (
        <div ref={elRef}>
          <button onClick={handleClick}>Click me</button>
        </div>
      );
    };

By using the useRef hook, you can access and manipulate referenced elements or values directly without triggering a re-render, making it useful for tasks that require imperative or low-level operations within your React components.

useContext hook

The useContext hook is used to access the value of a Context within a functional component. Context provides a way to pass data through the component tree without manually passing props at every level. The useContext hook allows you to consume that data within your components. Here's how you can use the useContext hook:

  1. Create a Context using the createContext function:

    import React, { createContext } from 'react';
    const DataContext = createContext();
    
  2. Wrap the components that need access to the Context data with a Provider component:

    const DataComponent = () => {
      let items = [ /* your items list here */ ]
      return (
        <DataContext.Provider value={ items}>
          {/* Child Components goes here */}
        </DataContext.Provider>
      );
    };

    In the above code we put the components that need access to the DataContext data with the DataContext.Provider component. The value prop is used to provide the value that will be accessible to the components.

  3. Within your functional component, call the useContext hook and pass the Context object as the argument. It returns the current value of the Context.

    const ChildComponent = () => {
      const dataItems = useContext(DataContext);
    
      // use the context data here
    };

    In the above code, we call the useContext hook within the ChildComponent functional component and pass the DataContext object as the argument. The returned dataItems will be the current value of the Context.

  4. You can now use the dataItems within your component as needed:

    const ChildComponent = () => {
      const dataItems = useContext(DataContext);
    
      return (
        <div>
            {dataItems.map((item)=>{
                <p>{item}</p>
            })}          
        </div>
      );
    };

    The useContext hook allows you to access the value of a Context directly within functional components without the need for a Context consumer component. It simplifies the consumption of Context data and makes your code more concise and readable.

useReducer hook

The useReducer hook is used to manage more complex state logic using a reducer function. It is used to define the state in a Redux application. Here's how you can use the useReducer hook:

  1. Import the useReducer hook from the React library:

    import React, { useReducer } from 'react';
  2. Define a reducer function that takes the current state and an action as arguments and returns the new state. The reducer function follows the same pattern as the reducer function in Redux.

    const reducer = (state, action) => {
      // Perform state transition based on the action type
      switch (action.type) {
        case 'INCREMENT':
          return { count: state.count + 1 };
        case 'DECREMENT':
          return { count: state.count - 1 };
        default:
          return state;
      }
    };

    The above reducer method defines a set of actions that modifies the state. The reducer function takes the current state (state) and an action (action) as arguments and returns the new state.

  3. Within your functional component, call the useReducer hook and provide the reducer function and the initial state as arguments. It returns the current state and a dispatch function to trigger state updates.

    const HelloComponent = () => {
      const initialState = { count: 0 };
      const [state, dispatch] = useReducer(reducer, initialState);
    
      // ...
    };

    When we call the userReducer hook method we pass the reducer object and the initial state object as arguments. It returs a pair of state object and dispatch function. The state variable holds the current state, and the dispatch function is used to trigger state updates.

  4. You can now use the state and dispatch within your component to access the current state and trigger state transitions:

    const HelloComponent = () => {
      const initialState = { count: 0 };
      const [state, dispatch] = useReducer(reducer, initialState);
    
      const increment = () => {
        dispatch({ type: 'INCREMENT' });
      };
    
      const decrement = () => {
        dispatch({ type: 'DECREMENT' });
      };
    
      return (
        <div>
          <p>Count: {state.count}</p>
          <button onClick={increment}>Increment</button>
          <button onClick={decrement}>Decrement</button>
        </div>
      );
    };

    The useReducer hook provides a structured approach to handling state updates and promotes code predictability and maintainability.

useMemo hook

The useMemo hook is used to memoize expensive calculations or computations within a functional component. It allows you to optimize performance by caching the result of a function or value and only recomputing it when the dependencies change.

  1. Import the useMemo hook from the React library:

    import React, { useMemo } from 'react';
  2. Within the component, call the useMemo hook and provide a callback function and an array of dependencies as arguments. The callback function will be executed to compute the memoized value, and the dependencies determine when the value should be recomputed.

    const DataComponent = () => {
    
      const memoValue = useMemo(() => {
        let result;
        // Perform the expensive calculation here and store in result    
        return result;
      }, [dependencies]);
    
      // ...
    };

    The callback function passed to useMemo will be executed to compute the memoized value. The value will be recalculated only when the dependencies specified in the array change.

  3. Within the callback function, perform the expensive calculation or computation that you want to memoize. The result of this computation will be returned as the memoized value.

    const DataComponent = () => {
      const memoValue = useMemo(() => {
        // Expensive calculation or computation
        const result = myExpensiveCalculation(dependencyValue);
        return result;
      }, [dependencyValue]);
    
      // ...
    };
  4. The memoized value, memoValue, can now be used within your component:

    const DataComponent = () => {
    
      const memoValue = useMemo(() => {
        const result = myExpensiveCalculation(dependencyValue);
        // ...
    
        return result;
      }, [dependencyValue]);
    
      return (
        <div>
          <p>Memoized Value: {memoValue}</p>
        </div>
      );
    };

    By using the useMemo hook, you can optimize the performance of your components by avoiding unnecessary recalculations of expensive computations. The memoized value is stored and reused as long as the dependencies remain the same, reducing the computational overhead and improving the efficiency of your application.

useCallback hook

Usually in React when a parent component re-renders, it re-renders all its child components also. It will be an additional overhead if the child component does not updates its template. To improve the performance of the application we can avoid the re-rendering of child components if it does not updates the values. The useCallback hook is used to memoize a callback function and prevent unnecessary re-rendering of child components that depend on that function. It is particularly useful when passing callbacks to child components that rely on stable references to avoid unnecessary re-rendering.

  1. Import the useCallback hook from the React library:

    import React, { useCallback } from 'react';
    
  2. Within your functional component, call the useCallback hook and provide a callback function and an array of dependencies as arguments. The callback function will be memoized and only re-created when the dependencies change.

    function ContactComponent() {
    
      const contactSelectionHandler = useCallback((/* arguments */)=>{
        // logic here
      }, [/*dependencies */])
    
      return (
        <div className="container">
          // Child components render here    
        </div>
      );
    }

    The callback function passed to useCallback will be memoized, and it will only be re-created when the dependencies specified in the array change.

  3. The memoized callback, contactSelectionHandler, can now be used within your component or passed down to child components:

    import { useCallback, useState } from "react";
    
    function ContactComponent() {
      const [branch,setBranch] = useState();
    
      const contactSelectionHandler = useCallback((country)=>{
        setBranch(country);
      }, [branch])
    
      return (
        <div className="container">
          <ContactInfo contactSelector={contactSelectionHandler} />
          {branch && <h4>Thank you for choosing {branch } branch </h4>}     
        </div>
      );
    }
  4. In the child component we can invoke the callback method to update the dependency variable (eg: branch). Return the memoized child component.

    import { memo } from "react";
    
    function ContactInfo({contactSelector}) {
    
      return (
        <div className="container mb-10">
          <h2>Contact Us</h2>      
          <div className="row">
            <div className="col-auto">
              // some info here
              <button className="btn btn-primary" onClick={()=>contactSelector('IN')}>Visit</button>
            </div>
            <div className="col-auto">
              // Some other info here
              <button className="btn btn-primary" onClick={()=>contactSelector('US')}>Visit</button>
            </div>
          </div>
        </div>
      );
    }
    
    export default memo(ContactInfo)

By using the useCallback hook, you can prevent unnecessary re-renders of child components that depend on a callback function. The callback function will be memoized and retain its stable reference as long as the dependencies remain the same. This helps optimize performance by avoiding unnecessary re-rendering of components that don't require it, improving the overall efficiency of your React application.

useSelector hook

The useSelector hook is a function provided by the React Redux library. It allows you to extract data from the Redux store in a React component. The useSelector hook subscribes to the Redux store, and whenever the store's state changes, it automatically re-renders the component with the updated data.

  1. Import the useSelector hook from the React Redux library:

    import { useSelector } from 'react-redux';
    
  2. Within your functional component, call the useSelector hook and provide a selector function as an argument. The selector function determines which part of the Redux store's state you want to extract.

    const HomeComponent = () => {
      const data = useSelector(selectorFunction);
    
      // ...
    };

    The selectorFunction is a function that takes the entire Redux store's state as an argument and returns the specific data you want to extract.

  3. Define a selector function that extracts the desired data from the Redux store's state:

    const selectorFunction = (state) => {
      return state.someData;
    };
  4. The extracted data, data, can now be used within your component:

    const HomeComponent = () => {
      const data = useSelector(selectorFunction);
    
      return (
        <div>
          <p>Data: {data}</p>
        </div>
      );
    };

The useSelector hook simplifies the process of accessing and using data from the Redux store in your React components. It automatically subscribes to the store's state updates and ensures that your component re-renders whenever the relevant data changes. This helps keep your component's UI in sync with the Redux store, making it easier to build and maintain complex React applications with predictable state management.

useDispatch hook

The useDispatch hook is a function provided by the React Redux library. It allows you to dispatch actions to the Redux store in a React component. The useDispatch hook provides a reference to the dispatch function, which you can use to trigger state updates by dispatching actions.

  1. Import the useDispatch hook from the React Redux library:

    import { useDispatch } from 'react-redux';
    
  2. Within your functional component, call the useDispatch hook to get a reference to the dispatch function:

    const HomeComponent = () => {
      const dispatch = useDispatch();
    
      // ...
    };
  3. Use the dispatch function to dispatch actions to the Redux store:

    const HomeComponent = () => {
      const dispatch = useDispatch();
    
      const handleClick = () => {
        dispatch({ type: 'SOME_ACTION', payload: someData });
      };
    
      return (
        <div>
          <button onClick={handleClick}>Do Something</button>
        </div>
      );
    };

    The action is an object with a type property that represents the action type, and a payload property that contains optional data for the action.

  4. The dispatch function can be used to trigger state updates by dispatching actions to the Redux store.

By using the useDispatch hook, you can easily access the dispatch function in your React components and trigger actions to update the Redux store. This simplifies the process of managing state in your components and allows you to interact with the Redux store seamlessly.

useNavigate hook

In React Router v6, the useNavigate hook is available to programmatically navigate between different routes within your application. It provides a convenient way to navigate without using the traditional <Link> or <NavLink> components. Here's how you can use the useNavigate hook:

  1. Make sure you have React Router v6 installed in your project. You can install it using npm or yarn:

    npm install react-router@next
    

    or

    yarn add react-router@next
    
  2. Import the useNavigate hook from the react-router-dom package:

    import { useNavigate } from 'react-router-dom';
    
  3. Within your functional component, call the useNavigate hook to get the navigate function.Use the navigate function to navigate to a specific route programmatically:

    const HomeComponent = () => {
      const navigate = useNavigate();
    
      const handleButtonClick = () => {
        navigate('/about');
      };
    
      return (
        <div>
          <button onClick={handleButtonClick}>Go to About</button>
        </div>
      );
    };

By using the useNavigate hook, you can programmatically navigate between routes in your React Router v6 application without relying on <Link> or <NavLink> components. It provides a flexible and convenient way to handle navigation logic within your functional components.

useHistory hook

The useHistory hook is available to access the history object, which allows you to programmatically navigate and manipulate the browser history. It provides a way to interact with the history stack and control the navigation within your application.

  1. Import the useHistory hook from the react-router-dom package:

    import { useHistory } from 'react-router-dom';
    
  2. Within your functional component, call the useHistory hook to get the history object:

    const HomeComponent = () => {
      const history = useHistory();
    
       // ...
    };
    
  3. Use the history object to navigate, go back, or perform other operations:

    const HomeComponent = () => {
      const history = useHistory();
    
      const handleNavigation = () => {
        history.push('/about');
      };
    
      const handleGoBack = () => {
        history.goBack();
      };
    
      return (
        <div>
          <button onClick={handleNavigation}>Go to About</button>
          <button onClick={handleGoBack}>Go Back</button>
        </div>
      );
    };

In the above code, we call the useHistory hook to get the history object. We define a handleNavigation function that uses the push method of the history object to navigate to the desired route when the button is clicked. We also define a handleGoBack function that uses the goBack method to go back to the previous page.

By using the useHistory hook, you can access the history object and have full control over the navigation and history management in your React Router application. It allows you to programmatically navigate, go back, or manipulate the browser history to create a smooth and interactive user experience.