DAY 13 : Master React Context API & Custom Hooks + Build a Color Palette Generator | My Web Dev Journey (ReactJS)

Ritik KumarRitik Kumar
11 min read

Introduction

Welcome to Day 13 of my web development journey!
For the past few weeks, I’ve been diving into ReactJS after completing the fundamentals of HTML, CSS, and JavaScript.

I learnt about Context API, Custom Hook and build a Color Palette Generator Project which helps me to practice concepts like Event Handling, useState() Hook and useEffect() Hook.

I'm sharing my learning process in public to stay consistent and hopefully help others who are on a similar path.

Here’s what I learnt in last 3 days:

  • Day 10:
    → How to do Nested Data Fetching in react?
    → How to pass data from one page to another page in react router?

  • Day 11:
    → Context API
    → Implementation of Context API

  • Day 12:
    → Custom Hook in React
    → Color Palette Generator Project

Let’s break down each of these topics below 👇


1. How to do Nested Data Fetching in react?

In real-world applications, we often deal with nested API responses — data that contains other sets of data within it. Fetching and rendering this data properly is crucial for building dynamic and interactive UIs.

What is Nested Data?

Nested data is when a JSON object contains another object or array inside it.

{
  "user": {
    "id": 1,
    "name": "Ritik",
    "posts": [
      { "id": 101, "title": "React Basics" },
      { "id": 102, "title": "Understanding Hooks" }
    ]
  }
}

Here:

  • user is an object.
  • posts is a nested array inside user.

Goal:

  • Fetch the data using fetch() or axios.
  • Access nested parts of the data (like user.posts).
  • Display it properly using React components.

How to Handle Nested Data Fetching:

We can approach this with a single API call or multiple dependent calls.

Method 1: Single API Call (Handle Nested Data Directly)

import { useEffect, useState } from "react";

function NestedDataComponent() {
  const [userData, setUserData] = useState(null);

  useEffect(() => {
    fetch("https://api.example.com/user/1")
      .then((res) => res.json())
      .then((data) => setUserData(data));
  }, []);

  if (!userData) return <p>Loading...</p>;

  return (
    <div>
      <h2>User: {userData.user.name}</h2>
      <h3>Posts:</h3>
      <ul>
        {userData.user.posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

Here, We directly extract the nested data inside the component once the API response is fetched.

Method 2: Multiple Dependent API Calls

Sometimes, we fetch user data first, then fetch their posts using their ID.

import { useEffect, useState } from "react";

function NestedDataComponent() {
  const [user, setUser] = useState(null);
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    fetch("https://api.example.com/user/1")
      .then((res) => res.json())
      .then((data) => {
        setUser(data);
        return fetch(`https://api.example.com/user/${data.id}/posts`);
      })
      .then((res) => res.json())
      .then((postsData) => setPosts(postsData));
  }, []);

  if (!user) return <p>Loading...</p>;

  return (
    <div>
      <h2>{user.name}'s Posts</h2>
      <ul>
        {posts.map((p) => (
          <li key={p.id}>{p.title}</li>
        ))}
      </ul>
    </div>
  );
}

Useful when APIs are split (user data and post data come from different endpoints).

Tips for Handling Nested Data:

  • Use optional chaining (?.) to safely access deeply nested properties.
  • Break down UI into child components and pass nested data as props.
  • Handle null or undefined values to avoid runtime errors during initial render.

2. How to pass data from one page to another page in react router?

When navigating between pages using React Router, we may often want to pass data from one route to another. React Router makes this easy using the state object with the Link or navigate functions.

Using <Link> with State:

We can pass data using the state prop in the <Link> component.

// HomePage.jsx
import { Link } from "react-router-dom";

function HomePage() {
  const user = { name: "Ritik", age: 22 };

  return (
    <Link to="/profile" state={{ user }}>
      Go to Profile
    </Link>
  );
}

Receiving Data on Another Page:

Use the useLocation() hook to access the passed state.

// ProfilePage.jsx
import { useLocation } from "react-router-dom";

function ProfilePage() {
  const location = useLocation();
  const { user } = location.state || {};

  return (
    <div>
      <h2>Name: {user?.name}</h2>
      <p>Age: {user?.age}</p>
    </div>
  );
}

Notes:

  • The data passed this way is not visible in the URL.
  • It is only available during navigation.
  • Refreshing the page will lose the data unless it's stored in a state manager (like Redux, Context API) or localStorage/sessionStorage.

Use Cases:

  • Passing selected item details to a details page.
  • Passing user info between routes without refetching.
  • Navigating to a result page with form input data.

3. Context API:

The Context API in React is a built-in way to share data across components without passing props manually at every level.
It solves the problem of prop drilling and is great for managing global data like themes, user information, or language preferences.

What is Prop Drilling?

Prop drilling occurs when you pass data through multiple layers of components even when only the deeply nested child needs it.

<Parent user={user}>
  <Child user={user}>
    <GrandChild user={user} /> {/* Only this needs user */}
  </Child>
</Parent>

This can get messy as your app grows.
Context API solve this problem.

Why Do We Need Context API?

  • Avoids prop drilling
  • Cleaner and more manageable code
  • Centralizes the state/data management
  • Ideal for things like user authentication, themes, language settings, etc.

Implementation of Context API:

We'll use a UserContext as an example.

1. Create the Context:

// UserContext.js
import { createContext } from 'react';

// creating context
export const UserContext = createContext();

This creates a Context Object, which we can later use to Provide and Consume data in our React application.

2. Create a Context Provider Component:

// UserProvider.js
import React, { useState } from 'react';
import { UserContext } from './UserContext';

const UserProvider = ({ children }) => {
  const [user, setUser] = useState("Ritik");

  return (
    // passing state in value
    <UserContext.Provider value={{ user, setUser }}>
      {children}
    </UserContext.Provider>
  );
};

export default UserProvider;

This UserProvider component will wrap our entire app and provide access to the user state throughout the component tree.

3. Wrap Our App with Provider:

// App.js
import React from 'react';
import UserProvider from './UserProvider';
import Home from './Home';

const App = () => {
  return (
    <UserProvider>
      <Home />
    </UserProvider>
  );
};

export default App;

Every Component inside App can access user state.

4. Consume the Context in any Component:

// Home.js
import React, { useContext } from 'react';
import { UserContext } from './UserContext';

const Home = () => {
  // accessing state
  const { user, setUser } = useContext(UserContext);

  return <h1>Welcome, {user}!</h1>;
};

export default Home;

Here, we didn't pass user as props — yet we can access & update it.
This is the real power of the Context API.


4. Custom Hook in React:

A Custom Hook is a JavaScript function in React that starts with use and allows us to reuse stateful logic across multiple components.

It’s a way to extract and share logic that uses React features like useState, useEffect, etc., across our components without repeating code.

Why Do We Need Custom Hooks?

  • To avoid code duplication when multiple components share similar logic.
  • Helps to organize and clean up code.
  • Promotes reusability and separation of concerns.
  • Makes components more readable and focused on UI rendering.

How to Create a Custom Hook?

Let’s create a custom hook called useWindowWidth that tracks the current window width.

1. Create useWindowWidth.js:

import { useState, useEffect } from 'react';

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);

  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);

    window.addEventListener('resize', handleResize);

    // Cleanup the event listener on unmount
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return width;
}

export default useWindowWidth;

2. Use this custom hook in any component:

import useWindowWidth from './useWindowWidth';

function App() {
  const width = useWindowWidth();

  return (
    <div>
      <h2>Current Window Width: {width}px</h2>
    </div>
  );
}

Key Points:

  • A custom hook is just a function that can use other hooks.
  • The name must start with use (e.g., useWindowWidth).
  • We can use custom hooks inside any functional component.
  • Always handle cleanup inside useEffect if there are subscriptions or event listeners.

5. Color Palette Generator Project:

In this project, I built a Color Palette Generator using React that dynamically generates a set of random hex colors, displays them as cards, and allows users to copy color codes with a click.

What This Project Does:

  • Generates a fresh color palette of 5 random colors on page load and on button click.
  • Displays each color in a card with its corresponding hex code.
  • Allows the user to copy the hex code of any color to the clipboard.
  • Shows a ✔️ icon when a color is successfully copied.

Component Structure:

  • App.jsx: Renders the Home component.
  • Home.jsx: Core logic — handles state, color generation, and renders UI.
  • Button.jsx: A reusable generate button.
  • CardsList.jsx: Maps over the colors array and renders individual cards.
  • ColorCard.jsx: Displays color box, hex code, and handles copy functionality.

Step-by-Step Logic & Code:

1. App Component – Entry Point :

// App.jsx
import Home from "./Components/Home";

const App = () => {
  return (
    <>
      <Home />
    </>
  );
};

export default App;

Here, we render the Home component where the actual logic lives.

2. Home Component – Core Logic :

// Home.jsx
import { useEffect, useState } from "react";
import Button from "./Button";
import CardsList from "./CardsList";

const Home = () => {
  const [colors, setColors] = useState([]);

  const template = "0123456789ABCDEF";

  function getRandomColor() {
    let colorCode = "";
    for (let i = 0; i < 6; i++) {
      const randomNumber = Math.floor(Math.random() * template.length);
      colorCode += template[randomNumber];
    }
    return colorCode;
  }

  function handleGenerateColor() {
    const arr = Array.from({ length: 5 }, () => getRandomColor());
    setColors(arr);
  }

  useEffect(() => {
    handleGenerateColor();
  }, []);

  return (
    <>
      <h1>Color Palette Generator</h1>
      <Button onClick={handleGenerateColor} />
      <CardsList colors={colors} />
    </>
  );
};

export default Home;
Explanation:
  • colors stores the array of generated color codes.
  • getRandomColor() creates a 6-digit hex color by randomly selecting characters from the template string (0123456789ABCDEF).
  • handleGenerateColor() creates a new array of 5 random hex colors and updates the colors state.
  • useEffect() runs once when the component mounts and calls handleGenerateColor() to load the initial color palette.
  • The Component renders:
    • A heading (Color Palette Generator)
    • A generate button (to refresh palette)
    • A list of color cards (each displaying a hex color and a copy icon)

3. Button Component – Generate Button :

// Button.jsx
const Button = ({ onClick }) => {
  return (
    <button onClick={onClick} className="btn">
      <i className="fa-solid fa-rotate"></i> Generate Palette
    </button>
  );
};

export default Button;
Explanation:
  • Receives onClick prop from Home.
  • Calls handleGenerateColor() when clicked.
  • Uses FontAwesome icons for UI enhancement.

4. CardsList Component – List Wrapper :

// CardsList.jsx
import ColorCard from "./colorCard";

const CardsList = ({ colors }) => {
  return (
    <div className="color-card-container">
      {colors.map((color) => {
        return <ColorCard key={color} color={color} />;
      })}
    </div>
  );
};

export default CardsList;
Explanation:
  • Accepts the colors array as a prop.
  • Maps through the array and renders individual ColorCard components.

5. ColorCard Component – Color Box + Copy :

// ColorCard.jsx
import { useState } from "react";

const ColorCard = ({ color }) => {
  const [isCopied, setIsCopied] = useState(false);

  function handleCopy() {
    const text = `#${color}`;
    navigator.clipboard.writeText(text).then(() => {
      setIsCopied(true);

      setTimeout(() => {
        setIsCopied(false);
      }, 1000);
    });
  }

  return (
    <div className="color-card">
      <div className="color" style={{ backgroundColor: `#${color}` }}></div>
      <div className="color-code">
        <p>#{color}</p>
        <i
          onClick={handleCopy}
          className={`fa-solid ${isCopied ? "fa-check" : "fa-copy"}`}></i>
      </div>
    </div>
  );
};

export default ColorCard;
Explanation:
  • Each card displays a background color and its hex code.
  • Clicking the icon triggers handleCopy() which:
    • Uses navigator.clipboard.writeText() to copy the color.
    • Sets isCopied to true for 1 second to show check icon.
  • Uses conditional rendering for copy ✅ feedback:
    className={`fa-solid ${isCopied ? "fa-check" : "fa-copy"}`}
    

Features Implemented:

  • ✅ Random hex color generation
  • ✅ Dynamic rendering using state
  • ✅ Clipboard copy with icon feedback
  • ✅ Component-based architecture
  • ✅ FontAwesome icons

Challenges:

  • Managing temporary state for copied feedback (isCopied).
  • Ensuring unique colors while mapping without key warnings.

Key Learnings

  • Used useState, useEffect, and map() effectively.
  • Practiced lifting state up and passing props down.
  • Implemented clipboard copy using the Web API.
  • Improved UI feedback with conditional icons.

Final Thoughts:

This was a fun and practical mini-project to explore React hooks, state management, and component-based architecture. Features like copy-to-clipboard and dynamic rendering are small but real-world-useful.


6. What's next:

I’m continuing this journey and will be:

  • Posting blogs every 3 days
  • Also learning DSA using Java — check out my DSA Journey Blog
  • You can follow my journey on X (Twitter) where I post regular updates.

7. Conclusion:

In this part of my web development journey, I explored some fundamental and practical concepts that every React developer should understand:

  • 🔄 Nested Data Fetching — How to fetch and display deeply nested API data efficiently.
  • 📦 Passing Data Between Pages — Navigating routes and sharing data without cluttering the URL.
  • 🌐 Context API — Managing global state in a clean, scalable way while avoiding prop drilling.
  • 🧩 Custom Hooks — Reusing logic cleanly, like tracking window width, without repeating code.
  • 🎨 Color Palette Generator Project — A fun hands-on project that ties together multiple concepts like state, props, custom hooks, and component composition.

By understanding and implementing these concepts, I am not only building better applications, but also writing cleaner, reusable, and more maintainable code.
🚀 With every project and concept, I feel more confident in building full-featured applications using React!

Stay tuned as I continue learning and building more exciting projects on my web development journey.

If you're on a similar journey, feel free to reach out or follow along — we’re all in this together.

0
Subscribe to my newsletter

Read articles from Ritik Kumar directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ritik Kumar
Ritik Kumar

👨‍💻 Aspiring Software Developer | MERN Stack Developer.🚀 Documenting my journey in Full-Stack Development & DSA with Java.📘 Focused on writing clean code, building real-world projects, and continuous learning.