Introduction to React Hooks

Share with a friend:

React Hooks revolutionized the way developers write functional components in React. To learn more about React and its many use cases visit here.

Introduced in React 16.8, hooks allow developers to use state and other React features without writing a class. They provide a simpler and more concise way of managing state and side effects in functional components, making them easier to understand, test, and maintain. In this article, we will dive into the world of React Hooks and explore their capabilities.

Table of Contents

What are React Hooks?

React Hooks are functions that enable developers to use React features such as state, context, and lifecycle methods in functional components. Before hooks, these features were only available in class components. Hooks provide a way to reuse stateful logic between components and allow developers to write reusable and composable code.

Hooks follow two main principles:

  1. Hooks are functions: Hooks are regular JavaScript functions that use a specific naming convention. They typically start with the word “use,” such as useState or useEffect.
  2. Hooks let you “hook into” React features: Hooks provide a way to tap into the internal React state and lifecycle features from functional components.

Rules of React Hooks

  1. Only call hooks at the top level: Hooks should be called directly inside functional components or custom hooks, but not inside loops, conditions, or nested functions.
  2. Call hooks in the same order every time: The order in which hooks are called should remain consistent across renders of the component. This helps React keep track of the state associated with each hook correctly.
  3. Only call hooks from React functions: Hooks should only be called from within React components or custom hooks. Don’t call hooks from regular JavaScript functions.

Commonly Used React Hooks

1. useState

The useState hook is used to add state to functional components. It returns a pair of values: the current 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(prevCount => prevCount + 1)}>Increment</button>
    </div>
  );
}

In this example, we initialize the state count to 0 using the useState hook. We destructure the state value and the update function from the returned array. The onClick event handler updates the count state by calling setCount with the new value.

2. useEffect

The useEffect hook allows us to perform side effects in functional components. It takes a function as its first argument, which will be executed after the component renders. Here’s an example:

import React, { useState, useEffect } from 'react';

function Timer() {
  const [seconds, setSeconds] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      setSeconds(prevSeconds => prevSeconds + 1);
    }, 1000);

    return () => {
      clearInterval(interval);
    };
  }, []);

  return (
    <div>
      <p>Seconds: {seconds}</p>
    </div>
  );
}

In this example, we use useEffect to start a timer that increments the seconds state every second. We return a cleanup function from the effect to clear the interval when the component unmounts. The empty dependency array ([]) ensures that the effect runs only once, similar to componentDidMount.

3. useContext

The useContext hook is used to consume values from the React context. It accepts a context object created by React.createContext and returns the current context value. Here’s an example:

import React, { createContext, useContext } from 'react';

// Step 1: Create a context
const MyContext = createContext();

// Step 2: Create a provider component
function MyProvider({ children }) {
  // Define the shared state or data
  const sharedData = 'Hello from context!';

  return (
    <MyContext.Provider value={sharedData}>
      {children}
    </MyContext.Provider>
  );
}

// Step 3: Use the context in a consumer component
function MyConsumer() {
  const sharedData = useContext(MyContext);

  return <p>{sharedData}</p>;
}

// Step 4: Use the provider and consumer in your app
function App() {
  return (
    <MyProvider>
      <div>
        <h1>My App</h1>
        <MyConsumer />
      </div>
    </MyProvider>
  );
}

In this example, we use the provider-consumer pattern to share data or state across multiple components without having to pass props manually through the component tree. The provider component provides the data, and the consumer component consumes it wherever needed within the descendant components by simply using the useContext hook.

4. useRef

The useRef hook returns a mutable ref object whose .current property can be used to store a value. It allows us to access and manipulate the underlying DOM or React element. Here’s an example:

import React, { useRef } from 'react';

function TextInput() {
  const inputRef = useRef(null);

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

In this example, we create a ref using useRef and assign it to the inputRef variable. The ref is then attached to the input element using the ref attribute. When the button is clicked, the focusInput function is called, which focuses the input element using the ref’s .current property.

Also, the useRef hook in React provides a way to persist a value across multiple renders of a functional component. Unlike state variables (useState), useRef does not trigger re-renders when its value changes. This unique characteristic of useRef makes it suitable for storing values that need to persist across renders without affecting the rendering process.

import React, { useRef } from 'react';

function Counter() {
  const counterRef = useRef(0);

  const increment = () => {
    counterRef.current += 1;
    console.log(counterRef.current);
  };

  return (
    <div>
      <p>Count: {counterRef.current}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

In the code snippet above, the counterRef is initialized using the useRef hook with an initial value of 0. On subsequent renders, even if the component re-renders due to other state or prop changes, the counterRef.current value remains persistent. It retains the value previously assigned to it.

The increment function demonstrates how we can modify the value stored in the counterRef. By accessing counterRef.current and updating its value, we can keep track of the count without triggering a re-render. The updated value is readily available in subsequent renders or function calls without affecting the rendering process.

Building Custom Hooks

Custom hooks are reusable functions that can encapsulate and share stateful logic between components. By convention, custom hooks should start with the word “use” to signal that they follow the rules of hooks.

Here’s an example of a custom hook that manages a toggle state:

import { useState } from 'react';

function useToggle(initialValue = false) {
  const [value, setValue] = useState(initialValue);

  const toggle = () => {
    setValue(prevValue => !prevValue);
  };

  return [value, toggle];
}

In this example, we define a custom hook called useToggle that initializes a boolean state and provides a toggle function to update it. We use the useState hook internally to manage the state. The custom hook returns an array with the current value and the toggle function.

Conclusion

React Hooks have brought a new level of simplicity and reusability to functional components in React. They allow developers to leverage state and other React features without using class components. With hooks, managing state, performing side effects, and sharing logic between components has become more straightforward and concise. By understanding and using hooks effectively, developers can build cleaner, more maintainable React applications.

Share with a friend:

Rajae Robinson

Rajae Robinson is a young Software Developer with over 3 years of work experience building websites and mobile apps. He has extensive experience with React.js and Next.js.

Recent Posts