DEV Community

cristinalynn
cristinalynn

Posted on

Let's talk about Hooks in React

Introduction:

Before talking about Hooks in React, let's briefly talk about what React is. React is an open-source JavaScript library used for building user interfaces. In order to understand React better, you should have a basic understanding of JavaScript. React follows a component-based architecture, where the user interface is divided into reusable, self-contained components. These components are responsible for rendering a part of the UI (user interface) and managing their own state and behavior. React provides a declarative syntax, allowing developers to describe how the UI should look based on the current state, and React knows how to efficiently update and render the components when the state changes.

Hooks in React are a feature introduced in React 16.8 to provide state and other React features in functional components. Prior to the introduction of hooks, state and life cycle functionality were only available in class components. With hooks, functional components can now have their own state and life cycle behavior without needing to convert them into class components.

Hooks:

Hooks are functions that let you “hook into” React state and life cycle features from function components. Hooks don’t work inside classes. Hooks provide a way to reuse stateful logic between components, making code more modular and easier to understand. They allow you to write reusable and composable code without the need for class components and their associated complexities.

React provides a few built-in hooks. In this blog I am going to be talking about the useState hook and the useEffect hook.

  • useState: The useState hook allows functional components to manage state. It returns a state value and a function to update that value. Here's an example:
import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

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

The example above renders a counter. When the user clicks the button, it increments the value. We call useState inside a function component to add some local state to it. React will preserve this state between re-renders. useState returns a pair: the current state value and a function that lets you update it. You can call this function from an event handler or somewhere else. The only argument to useState is the initial state. In the example above, it is 0 because our counter starts from zero.

The State hook can be used more than once in a single component:

function ExampleWithManyStates() {
  // Declare multiple state variables!
  const [age, setAge] = useState(42);
  const [food, setFood] = useState('burger');
  const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
  // ...
}
Enter fullscreen mode Exit fullscreen mode
  • useEffect: The useEffect hook allows functional components to perform side effects, such as data fetching, subscriptions, or modifying the DOM. It accepts a function that runs after every render, and an optional array of dependencies to control when the effect should run. Here's an example:
import React, { useEffect, useState } from 'react';

function DataFetching() {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Data fetching logic
    fetch('https://api.example.com/data')
      .then(response => response.json())
      .then(data => setData(data));
  }, []);

  return <div>Data: {JSON.stringify(data)}</div>;
}

Enter fullscreen mode Exit fullscreen mode

By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In the code above, notice that we have an empty array at the end of the useEffect. We need to pass a second argument to useEEffect, a dependencies array, in order to prevent an endless loop of fetch requests.

Effect cleanup:

Some effects require cleanup to reduce memory leaks. Timeouts, subscriptions, event listeners, and other effects that are no longer needed should be disposed. We do this by including a return function at the end of the useEffect Hook. Here is an example:

import { useState, useEffect } from "react";
import ReactDOM from "react-dom/client";

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    let timer = setTimeout(() => {
    setCount((count) => count + 1);
  }, 1000);

  return () => clearTimeout(timer)
  }, []);

  return <h1>I've rendered {count} times!</h1>;
}

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Timer />);
Enter fullscreen mode Exit fullscreen mode

Conclusion
Hooks provide a simpler and more intuitive way to manage state and side effects in functional components. They have become the recommended approach for writing React components and have gained widespread adoption in the React community. They are JavaScript functions, but you need to follow two rules when using them:

  • Only call hooks at the top level! Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.

  • Only call hooks from React functions! Don’t call Hooks from regular JavaScript functions. Instead, you can call Hooks from React function components.

Top comments (0)