Skip to main content

Command Palette

Search for a command to run...

[Part-2] Advanced React Techniques: Exploring Hooks and Context API

Published
5 min read
[Part-2] Advanced React Techniques: Exploring Hooks and Context API
A

Anisha Swain | The UI Girl Hello world! Anisha this side👋

💪Making @theuigirl

💻 speaker http://t.ly/9D22 🍀 1:1 https://topmate.io/anishaswain 🎙️ podcast host http://t.ly/_MUml ✏️ blog https://medium.com/the-ui-girl 🧳 Travel story http://t.ly/Xa-5v

https://bento.me/anishaswain Let's connect 🤝

React, with its declarative and component-based approach, has been a game-changer in front-end development. With the introduction of Hooks and the Context API, React has empowered developers to write more concise, readable, and reusable code. In this article, we'll dive deep into these advanced React techniques, explore how they work, and see how they can be leveraged to build powerful and maintainable applications.

Understanding Hooks

Introduction to Hooks

Prior to the introduction of Hooks in React 16.8, functional components were limited in terms of state and lifecycle management. Hooks provide a way to use state and other React features in functional components.

useState Hook

useState is one of the most commonly used Hooks. It allows functional components to have local state.

import React, { useState } from 'react';

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

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

In the example above, useState is used to declare a state variable count and a function setCount to update its value.

useEffect Hook

useEffect is used to perform side effects in functional components, such as data fetching, subscriptions, or manually changing the DOM.

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

const DataFetcher = () => {
  const [data, setData] = useState(null);

  useEffect(() => {
    // Fetch data when the component mounts
    fetchData()
      .then((result) => setData(result))
      .catch((error) => console.error(error));
  }, []); // Empty dependency array means this effect runs once on mount

  return (
    <div>
      <p>Data: {data}</p>
    </div>
  );
};

useContext Hook

useContext allows functional components to subscribe to React context without introducing nesting.

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

// Creating a context
const ThemeContext = createContext();

// Using a provider to wrap components
const App = () => {
  return (
    <ThemeContext.Provider value="light">
      <ThemedComponent />
    </ThemeContext.Provider>
  );
};

// Consuming the context with useContext
const ThemedComponent = () => {
  const theme = useContext(ThemeContext);

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

Custom Hooks

Custom Hooks are a way to reuse stateful logic between components. A custom Hook is a JavaScript function whose name starts with use and may call other Hooks.

import { useState, useEffect } from 'react';

const useDataFetching = (url) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(url);
        const result = await response.json();
        setData(result);
      } catch (error) {
        setError(error);
      } finally {
        setLoading(false);
      }
    };

    fetchData();
  }, [url]);

  return { data, loading, error };
};

In the example above, useDataFetching is a custom Hook that encapsulates the logic for fetching data. It returns an object with data, loading, and error states.

Exploring Context API

Introduction to Context API

The Context API provides a way to pass data through the component tree without having to pass props down manually at every level. It's especially useful for global data like themes, user authentication, or language preferences.

Creating a Context

Creating a context involves calling the createContext function. This returns a Provider component and a Consumer component.

import { createContext } from 'react';

const ThemeContext = createContext();

Providing and Consuming Context

A Provider component is used to wrap the components that need access to the context. The value provided by the Provider will be consumed by the Consumer or the useContext Hook.

import { createContext, useContext } from 'react';

const ThemeContext = createContext();

const App = () => {
  return (
    <ThemeContext.Provider value="dark">
      <ThemedComponent />
    </ThemeContext.Provider>
  );
};

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

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

Dynamic Context

Context values can be dynamic, meaning they can depend on the component's state or other props.

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

const ThemeContext = createContext();

const App = () => {
  const [theme, setTheme] = useState('light');

  const toggleTheme = () => {
    setTheme((prevTheme) => (prevTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <ThemedComponent />
    </ThemeContext.Provider>
  );
};

const ThemedComponent = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);

  return (
    <div>
      <p>Current Theme: {theme}</p>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

In this example, the ThemeContext provides both the theme and the toggleTheme function to the consuming components.

Context with Reducer

To handle more complex state logic, the Context API can be combined with a reducer function using the useReducer Hook.

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

const initialState = {
  count: 0,
};

const reducer = (state, action) => {
  switch (action.type) {
    case 'increment':
      return { count: state.count + 1 };
    case 'decrement':
      return { count: state.count - 1 };
    default:
      return state;
  }
};

const CounterContext = createContext();

const App = () => {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <CounterContext.Provider value={{ state, dispatch }}>
      <Counter />
    </CounterContext.Provider>
  );
};

const Counter = () => {
  const { state, dispatch } = useContext(CounterContext);

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

In this example, the CounterContext provides both the state and the dispatch function from the reducer.

Conclusion

Hooks and the Context API have transformed the way we approach state management and global data in React applications. They enable more functional and modular components, making the codebase easier to read and maintain.

More from this blog

T

The UI Girl

60 posts

Hello world! Anisha this side👋
I am a software developer conference speaker 💻 and a podcast host 🎙️ Loves travelling and digital illustrations 🍀

bento.me/anishaswain Let's connect