What are React Hooks?

What are React Hooks?

React Hooks have changed the way we work with functional components in React. They provide the power of state and lifecycle characteristics to functional components, making them more dynamic and versatile. Before we dive in, Books are a uniquely portable magic so learn more about React in this Ebook

I wrote something about emergency funds essentials, check it out 😉

React Hooks are a mechanism to provide state and lifecycle features to functional components. Before Hooks, these features were limited to class components. Functional components can now accomplish much more.

Why Hooks?

Hooks were introduced to simplify React code. They make it easier to understand and organize your code. Hooks provide a more direct way to manage state and lifecycle events in functional components.

Advantages Over Class Components

Hooks revolutionized the way we handle state and lifecycle features in React components, particularly functional ones. Let's delve into the profound advantages they bring over their class component counterparts, unraveling how they enhance the clarity, conciseness, and maintainability of your code.

Conciseness

In class components, managing state and lifecycle methods often involves writing more boilerplate code. Hooks, on the other hand, streamline this process. Take the useState Hook as an example. It condenses state management into a single line, making your code concise and focused.

// Class Component 
class ExampleClass extends React.Component { 
constructor(props) { 
super(props); 
this.state = { 
count: 0, 
}; 
} // ... 
}
// Functional Component with useState Hook
import React, { useState } from 'react';

const ExampleFunctional = () => {
  const [count, setCount] = useState(0);
  // ...
};

Readability

Hooks introduce a more linear and readable flow to your functional components. The logic related to a specific piece of state or an effect stays localized, enhancing code comprehension. This contrasts with class components, where related logic might be scattered across multiple lifecycle methods.

// Class Component
class ExampleClass extends React.Component {
  componentDidMount() {
    // ...
  }
  componentDidUpdate() {
    // ...
  }
  componentWillUnmount() {
    // ...
  }
  // ...
}
// Functional Component with useEffect Hook
import React, { useEffect } from 'react';

const ExampleFunctional = () => {
  useEffect(() => {
    // componentDidMount and componentDidUpdate logic here
    return () => {
      // componentWillUnmount logic here
    };
  }, []); // Empty dependency array for componentDidMount behavior
  // ...
};

Maintainability

With Hooks, your codebase becomes more maintainable as related functionalities are encapsulated within individual Hooks. This modular approach facilitates updates, debugging, and collaboration. It reduces the cognitive load associated with understanding the intricate interplay of lifecycle methods in class components.

// Class Component
class ExampleClass extends React.Component {
  // ...
}

// Functional Components with Custom Hooks
import React from 'react';
import useCustomLogic from './useCustomLogic';

const ExampleFunctional = () => {
  const { data, isLoading, error } = useCustomLogic();
  // ...
};

Use Hooks to transform your React development experience, making your codebase not just functional but also enjoyable to work with. Hooks provide cleaner and more organized React apps, whether through the elegance of useState or the adaptability of useEffect.

Basic Hooks

UseState

The useState Hook stands out as a pivotal game-changer, particularly for functional components. Traditionally, state management was confined to class components, but with the introduction of Hooks, this powerful capability seamlessly extends to functional ones.

The magic of useState lies in its simplicity and effectiveness. It empowers developers to declare and manipulate state variables within their functional components effortlessly. Picture it as a tool that allows components to remember and dynamically update information, ushering in a new era of concise, readable, and well-organized React applications.

import React, { useState } from 'react';

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

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Explanation:

  • We import useState from React to use it.

  • count is our state variable, and setCount is a function to update it.

  • The initial value count is set to 0.

  • When the button is clicked, setCount is called to increment the count.

useEffect

The useEffect Hook handles side effects in functional components. It's perfect for tasks like data fetching, subscriptions, or manually changing the DOM. It’s like having a virtual assistant for your components, ensuring they stay responsive to changes in their surroundings.

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

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Explanation:

  • useEffect is used for side effects in the component.

  • In this example, it updates the document title whenever count changes.

  • The dependency array [count] ensures that the effect runs when count changes.

useContext

The useContext Hook simplifies using context in functional components. It provides a cleaner way to access and update shared state without prop drilling.

import React, { useContext } from 'react';

const ThemeContext = React.createContext('light');

function ThemedComponent() {
  const theme = useContext(ThemeContext);

  return <p>Current theme: {theme}</p>;
}

Explanation:

  • We create a context with a default value of 'light'.

  • useContext(ThemeContext) is used to access the current theme.

  • It automatically gets the value provided by the nearest ThemeProvider in the component tree.

Additional Hooks

useReducer

The useReducer Hook manages complex state logic. It's particularly useful when the next state depends on the previous one.

import React, { useReducer } from 'react';

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      throw new Error();
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <div>
      <p>Count: {state.count}</p>
      <button onClick={() => dispatch({ type: 'increment' })}>
        Increment
      </button>
      <button onClick={() => dispatch({ type: 'decrement' })}>
        Decrement
      </button>
    </div>
  );
}

Explanation:

  • useReducer is used for managing complex state logic.

  • reducer is a function that specifies how the state should change based on the action.

  • dispatch is a function used to trigger state changes by providing an action object.

useCallback and useMemo

The useCallback and useMemo Hooks optimize performance by memoizing functions and values.

import React, { useState, useCallback, useMemo } from 'react';

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

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  const doubledCount = useMemo(() => count * 2, [count]);

  return (
    <div>
      <p>Count: {count}</p>
      <p>Doubled Count: {doubledCount}</p>
      <button onClick={handleClick}>
        Increment
      </button>
    </div>
  );
}

Explanation:

  • useCallback memoizes the handleClick function, preventing unnecessary re-creation on each render.

  • useMemo memoizes the result of the count * 2 computation, preventing redundant calculations.

useRef

The useRef Hook interacts with DOM elements and persists values between renders.

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

function FocusInput() {
  const inputRef = useRef();

  useEffect(() => {
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} />;
}

Explanation:

  • useRef is used to create a reference to a DOM element or a mutable object.

  • inputRef is assigned to the ref attribute of the input element.

  • useEffect is used to focus the input element when the component mounts.

Custom Hooks

Custom Hooks allow you to reuse logic across components. Let's create a simple custom Hook for handling dark mode.

import { useState, useEffect } from 'react';

function useDarkMode() {
  const [isDarkMode, setIsDarkMode] = useState(false);

  useEffect(() => {
    // Logic to toggle dark mode
  }, [isDarkMode]);

  return [isDarkMode, setIsDarkMode];
}

Explanation:

  • We create a custom Hook named useDarkMode.

  • It returns the state variable isDarkMode and a function setIsDarkMode to toggle it.

  • The useEffect can contain logic to handle dark mode changes.

Advanced Hooks and Patterns

useEffect and Cleanup

Cleaning up side effects is crucial. The useEffect Hook allows us to handle cleanup by returning a function.

import React, { useEffect } from 'react';

function ExampleComponent() {
  useEffect(() => {
    // Do something on mount
    return () => {
      // Clean up on unmount
    };
  }, []);
}

Explanation:

  • The function inside useEffect runs on mount.

  • The returned function runs on unmount, providing a way to clean up resources.

useLayoutEffect

useLayoutEffect is similar to useEffect but fires synchronously after all DOM mutations.

import React, { useLayoutEffect } from 'react';

function LayoutEffectExample() {
  useLayoutEffect(() => {
    // Do something after the DOM has been painted
  }, []);
}

Explanation:

  • useLayoutEffect is useful when you need to perform actions that require the DOM to be painted immediately after changes.

useImperativeHandle and useDebugValue

These advanced Hooks allow fine-grained control over the instances returned by useRef and debugging custom Hooks.

import React, { useRef, useImperativeHandle, useDebugValue } from 'react';

function CustomComponent() {
  const ref = useRef();

  useImperativeHandle(ref, () => ({
    // Expose certain properties or functions
  }));

  useDebugValue('Custom Component');

  return <div ref={ref}>I'm a custom component!</div>;
}

Explanation:

  • useImperativeHandle lets you customize the instance value returned by useRef.

  • useDebugValue provides additional information for debugging during development.

Best Practices and Tips

Effective Usage Tips

  • Keep Hooks at the top level of your component.

  • Name your custom Hooks starting with "use" to follow the convention.

Common Mistakes

  • Forgetting to add dependencies in the dependency array of useEffect.

  • Using Hooks conditionally can lead to bugs.

Code Organization

  • Group related Hooks together.

  • Create a separate file for custom Hooks for better organization.

Conclusion

In this guide, we covered the fundamental and advanced concepts of React Hooks. Now that you understand React Hooks, dive into your projects and start experimenting. The best way to learn is by doing!

Wish you good luck coding but before you go, check out the following books to improve as a programmer

JavaScript and jQuery: Interactive Front-End Web Development

A Smarter Way to Learn JavaScript

SEO Playbook for Getting Your Website Found on Google

Python Crash Course

"The more you read, the more things you will know. The more that you learn, the more places you will go." (Dr. Seuss)

Did you find this article valuable?

Support James Oluwaleye by becoming a sponsor. Any amount is appreciated!