React Hooks

Introduction

In this chapter, we will explore React Hooks, a feature that allows you to use state and other React features in functional components. Hooks were introduced in React 16.8 to provide a way to use state and lifecycle methods without writing class components. We will cover the most commonly used hooks, such as useState, useEffect, and useContext, with practical examples to demonstrate their usage.

Table of Contents

  1. What are React Hooks?
  2. Using useState
    • Example: Counter
  3. Using useEffect
    • Example: Fetching Data
  4. Using useContext
    • Example: Theme Context
  5. Custom Hooks
    • Example: useLocalStorage Hook
  6. Conclusion

What are React Hooks?

React Hooks allow you to use state and other React features in functional components. They enable you to manage component state, handle side effects, and access context in a more concise and readable way. Hooks follow a set of rules, such as only calling hooks at the top level of your component or custom hook and only calling them from React function components or custom hooks.

Using useState

The useState hook lets you add state to functional components. It returns an array with the current state value and a function to update that state.

Example: Counter

  1. Create a Counter Component: In the src directory, create a new file named Counter.js.
  2. Define the Counter Component: Add the following code to Counter.js:
    // src/Counter.js
    import React, { useState } from 'react';
    
    function Counter() {
      const [count, setCount] = useState(0);
    
      return (
        <div className="container mt-5">
          <h2>Counter</h2>
          <p>Current Count: {count}</p>
          <button className="btn btn-primary" onClick={() => setCount(count + 1)}>
            Increment
          </button>
        </div>
      );
    }
    
    export default Counter;
    

    Explanation:

    • We use the useState hook to create a count state variable and a setCount function to update it.
    • The button calls setCount with the current count incremented by 1 each time it is clicked.
  3. Use the Counter Component in App.js: Open src/App.js and import the Counter component:
    // src/App.js
    import React from 'react';
    import Counter from './Counter';
    
    function App() {
      return (
        <div>
          <Counter />
        </div>
      );
    }
    
    export default App;
    
  4. Save and View Changes: Save the files and go to your browser. You should see a counter that increments when you click the button.

Using useEffect

The useEffect hook lets you perform side effects in functional components. It runs after the first render and after every update, but you can control when it runs by specifying dependencies.

Example: Fetching Data

  1. Create a DataFetching Component: In the src directory, create a new file named DataFetching.js.
  2. Define the DataFetching Component: Add the following code to DataFetching.js:
    // src/DataFetching.js
    import React, { useState, useEffect } from 'react';
    
    function DataFetching() {
      const [data, setData] = useState([]);
      const [loading, setLoading] = useState(true);
    
      useEffect(() => {
        fetch('https://jsonplaceholder.typicode.com/posts')
          .then((response) => response.json())
          .then((data) => {
            setData(data);
            setLoading(false);
          });
      }, []);
    
      return (
        <div className="container mt-5">
          <h2>Data Fetching</h2>
          {loading ? (
            <p>Loading...</p>
          ) : (
            <ul>
              {data.slice(0, 5).map((item) => (
                <li key={item.id}>{item.title}</li>
              ))}
            </ul>
          )}
        </div>
      );
    }
    
    export default DataFetching;
    

    Explanation:

    • We use the useState hook to create data and loading state variables.
    • We use the useEffect hook to fetch data from an API when the component mounts. The empty array [] as the second argument ensures the effect runs only once.
    • We display a loading message while the data is being fetched and render the data once it is loaded.
  3. Use the DataFetching Component in App.js: Open src/App.js and import the DataFetching component:
    // src/App.js
    import React from 'react';
    import Counter from './Counter';
    import DataFetching from './DataFetching';
    
    function App() {
      return (
        <div>
          <Counter />
          <DataFetching />
        </div>
      );
    }
    
    export default App;
    
  4. Save and View Changes: Save the files and go to your browser. You should see a list of data fetched from the API.

Using useContext

The useContext hook lets you access context values in functional components. It simplifies the process of consuming context compared to the Context.Consumer component.

Example: Theme Context

  1. Create a Theme Context: In the src directory, create a new file named ThemeContext.js.
  2. Define the Theme Context: Add the following code to ThemeContext.js:
    // src/ThemeContext.js
    import React, { createContext, useState } from 'react';
    
    const ThemeContext = createContext();
    
    export const ThemeProvider = ({ children }) => {
      const [theme, setTheme] = useState('light');
    
      return (
        <ThemeContext.Provider value={{ theme, setTheme }}>
          {children}
        </ThemeContext.Provider>
      );
    };
    
    export default ThemeContext;
    

    Explanation:

    • We create a ThemeContext using createContext.
    • The ThemeProvider component manages the theme state and provides it to the component tree.
  3. Create a ThemedComponent: In the src directory, create a new file named ThemedComponent.js.
  4. Define the ThemedComponent: Add the following code to ThemedComponent.js:
    // src/ThemedComponent.js
    import React, { useContext } from 'react';
    import ThemeContext from './ThemeContext';
    
    function ThemedComponent() {
      const { theme, setTheme } = useContext(ThemeContext);
    
      return (
        <div className={`container mt-5 ${theme === 'dark' ? 'bg-dark text-white' : 'bg-light text-dark'}`}>
          <h2>Themed Component</h2>
          <p>Current theme: {theme}</p>
          <button className="btn btn-secondary" onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
            Toggle Theme
          </button>
        </div>
      );
    }
    
    export default ThemedComponent;
    

    Explanation:

    • We use the useContext hook to access the theme value and setTheme function from the ThemeContext.
    • The button toggles the theme between light and dark.
  5. Use the ThemeProvider in App.js: Open src/App.js and import the ThemeProvider and ThemedComponent components:
    // src/App.js
    import React from 'react';
    import Counter from './Counter';
    import DataFetching from './DataFetching';
    import { ThemeProvider } from './ThemeContext';
    import ThemedComponent from './ThemedComponent';
    
    function App() {
      return (
        <ThemeProvider>
          <Counter />
          <DataFetching />
          <ThemedComponent />
        </ThemeProvider>
      );
    }
    
    export default App;
    
  6. Save and View Changes: Save the files and go to your browser. You should see the themed component with a button to toggle the theme.

Custom Hooks

Custom hooks allow you to extract and reuse logic in functional components. They are JavaScript functions that start with the word “use” and can call other hooks.

Example: useLocalStorage Hook

  1. Create a Custom Hook: In the src directory, create a new file named useLocalStorage.js.
  2. Define the Custom Hook: Add the following code to useLocalStorage.js:
    // src/useLocalStorage.js
    import { useState } from 'react';
    
    function useLocalStorage(key, initialValue) {
      const [storedValue, setStoredValue] = useState(() => {
        try {
          const item = window.localStorage.getItem(key);
          return item ? JSON.parse(item) : initialValue;
        } catch (error) {
          console.log(error);
          return initialValue;
        }
      });
    
      const setValue = (value) => {
        try {
          const valueToStore = value instanceof Function ? value(storedValue) : value;
          setStoredValue(valueToStore);
          window.localStorage.setItem(key, JSON.stringify(valueToStore));
        } catch (error) {
          console.log(error);
        }
      };
    
      return [storedValue, setValue];
    }
    
    export default useLocalStorage;
    

    Explanation:

    • The useLocalStorage hook manages state that is synchronized with localStorage.
    • It initializes the state from localStorage and updates localStorage whenever the state changes.
  3. Use the Custom Hook: In the src directory, create a new file named LocalStorageComponent.js.
  4. Define the LocalStorageComponent: Add the following code to LocalStorageComponent.js:
    // src/LocalStorageComponent.js
    import React from 'react';
    import useLocalStorage from './useLocalStorage';
    
    function LocalStorageComponent() {
      const [name, setName] = useLocalStorage('name', '');
    
      return (
        <div className="container mt-5">
          <h2>Local Storage Hook</h2>
          <input
            type="text"
            className="form-control"
            value={name}
            onChange={(e) => setName(e.target.value)}
            placeholder="Enter your name"
          />
          <p>Your name is: {name}</p>
        </div>
      );
    }
    
    export default LocalStorageComponent;
    

    Explanation:

    • The LocalStorageComponent uses the useLocalStorage hook to manage a name input synchronized with localStorage.
  5. Use the LocalStorageComponent in App.js: Open src/App.js and import the LocalStorageComponent component:
    // src/App.js
    import React from 'react';
    import Counter from './Counter';
    import DataFetching from './DataFetching';
    import { ThemeProvider } from './ThemeContext';
    import ThemedComponent from './ThemedComponent';
    import LocalStorageComponent from './LocalStorageComponent';
    
    function App() {
      return (
        <ThemeProvider>
          <Counter />
          <DataFetching />
          <ThemedComponent />
          <LocalStorageComponent />
        </ThemeProvider>
      );
    }
    
    export default App;
    
  6. Save and View Changes: Save the files and go to your browser. You should see the Local Storage component with an input field synchronized with localStorage.

Conclusion

In this chapter, we explored React Hooks and how they allow you to use state and other React features in functional components. We covered the most commonly used hooks, such as useState, useEffect, and useContext, with practical examples. We also demonstrated how to create custom hooks to extract and reuse logic in your applications. Understanding and using hooks can help you write cleaner and more efficient React components.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top