DEV Community

Cover image for Hooks in React
Deepansh Bhargava
Deepansh Bhargava

Posted on

Hooks in React

Today we'll learn about the widely used hooks in React. First, let me tell you the need for hooks. As we all know React has functional and class components. Previously, the functional components were only used to render the data but didn't consist of the business logic or any side effect in itself.

So hooks were introduced to implement lifecycle methods and states in functional components. They also bought the idea of code reusability along with them, that's why it was so quickly accepted within the React Community.

** Drum rolls 🥁 **

Let's move ahead with the main topic.

  • useState hook

This hook helps us to use and maintain a state within the functional component. It's very easy to use. The useState hook returns an array having two elements, the first one is the state variable and the second is the function that modifies the state variable.

import { useState } from "react";

export default function App() {
  const [number, setNumber] = useState(0);

  return (
    <div className="App">
      <button onClick={() => setNumber(number - 1)}>Subtract</button>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <button onClick={() => setNumber(number + 1)}>Add</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here, we have number as the state variable and setNumber as the function which will modify the number.

  • useEffect hook

This hook works as a replacement for all the lifecycle methods which were used in class components. It contains a callback and a dependencies array. The hook will run every time when any of the values in the dependencies array changes.

import { useEffect, useState } from "react";

export default function App() {
  const [number, setNumber] = useState(0);
  const [isEven, toggleEven] = useState(true);

  useEffect(() => {
    if (number % 2 !== 0) {
      toggleEven(false);
    } else {
      toggleEven(true);
    }
  }, [number]);

  return (
    <div className="App">
      <button onClick={() => setNumber(number - 1)}>Subtract</button>
      <input
        type="number"
        value={number}
        onChange={(e) => setNumber(e.target.value)}
      />
      <button onClick={() => setNumber(number + 1)}>Add</button>
      <p>{isEven ? "Even" : "Odd"}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Here as you can see, the dependencies array contains number. So every time the user presses any of the buttons or changes the input value, the useEffect hook will trigger and it'll check the number whether it's odd or even.

Within this hook, you can call side effects such as changing something in DOM, fetching data from an API, etc.

One more thing used in this hook is the cleanup function, which runs when we return a function in the callback. This function helps in cleaning of side effects i.e clearing a timer, closing a web socket, etc.

  • useContext hook

As in the class components, we had the Context API in the same way, we have useContext hook in functional components. This hook basically helps you to maintain a common global state across components. When the state changes it triggers re-renders across all the child components under the Context, even if the parent component uses shouldComponentUpdate or React.memo.

This hook can be used to implement a loading state within the application, passing data to children components, etc.

To create it, we use the createContext function and pass the initial value. In the context provider, we pass a prop called value which consists of the context variables and functions to modify the context.

import { createContext } from "react";

export const UserContext = createContext({
  name: "Initial Name"
});                                                           
Enter fullscreen mode Exit fullscreen mode

Then we use this context in our Parent Component and wrap our app inside the Context Provider and pass a value. So now all the child components have the access to the value prop.

import { useContext, useState } from "react";
import Person from "./Person";
import { UserContext } from "./Context";

export default function App() {
  const user = useContext(UserContext);

  const [person, togglePerson] = useState(user);

  return (
    <UserContext.Provider value={[person, togglePerson]}>
      <div className="App">Hello {user.name}!</div>
      <Person />
    </UserContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode

Now in the child component Person we can access the value prop and we can change the context value too.

import { useContext } from "react";
import { UserContext } from "./Context";

function Person() {
  const [person, togglePerson] = useContext(UserContext);

  return (
    <div>
      Person: {person.name}
      <button
        onClick={() => {
          togglePerson({ name: "Stuart Little" });
        }}
      >
        Change
      </button>
    </div>
  );
}

export default Person;
Enter fullscreen mode Exit fullscreen mode

This way you can use the useContext hook. Make a context, wrap the parent component with the context's provider and you are good to go!

You can experiment with the above code here.

  • useReducer hook

If you know Redux then you already know what this hook does, if not then simply it changes the state according to the action passed. It helps in maintaining a local state easily whereas in Redux we can do that globally.

To use the reducer globally, we can make a context and pass it across the components. Geddit!

Here we have an initial state and a dispatch to modify the state. Within the dispatch, we pass actions along with some info called payload. The action then goes to the reducer which in turn changes the state.

import { useReducer } from "react";
import "./styles.css";

const initialState = [
  {
    name: "Mad Angles",
    quantity: 5
  },
  {
    name: "Pringles",
    quantity: 3
  },
  {
    name: "Lays",
    quantity: 2
  },
  {
    name: "Kurkure",
    quantity: 1
  },
  {
    name: "Uncle Chips",
    quantity: 9
  }
];

function reducer(state, action) {
  switch (action.type) {
    case "add":
      return [...state, action.payload];
    case "delete":
      return state.filter((_, index) => index !== action.payload);
    default:
      return state;
  }
}

export default function App() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <ul>
        {state.map(({ name, quantity }, index) => (
          <div className="list-item">
            <li key={name}>
              <b>{name}</b> - {quantity}
            </li>
            <button
              onClick={() => {
                dispatch({ type: "delete", payload: index });
              }}
            >
              delete
            </button>
          </div>
        ))}
        <button
          onClick={() => {
            dispatch({
              type: "add",
              payload: {
                name: "Doritoes",
                quantity: 5
              }
            });
          }}
        >
          add
        </button>
      </ul>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

You can find the above code here.

That's it for now. There are other hooks too like useRef, useCallback but they are rarely used. Thanks for reading this article. Let me know if anything is not clear.

Happy Coding!

Top comments (0)