Theme Switcher (context api)

  1. Dependencies and Imports:

    • useEffect and useState are React hooks.

    • ThemeProvider is imported from a custom context (./context/theme).

    • ThemeBtn and Card are components used within App.

  2. State Management:

    • A themeMode state is initialized with a default value "light".
  3. Theme Switch Handlers:

    • lightTheme: Sets the themeMode to "light".

    • darkTheme: Sets the themeMode to "dark".

  4. Theme Application Using useEffect:

    • Watches changes to themeMode.

    • Dynamically applies the theme by adding/removing classes (light or dark) to the <html> tag.

  5. Component Structure:

    • ThemeProvider: Wraps the child components, providing them access to themeMode, lightTheme, and darkTheme.

    • ThemeBtn: Button for toggling the theme.

    • Card: Example UI component (specific details not provided).

  6. Layout:

    • A basic flexbox layout is used to center-align the ThemeBtn and Card components.

Complete Code (with comments)

// **App.jsx**
import { useEffect, useState } from 'react'
import './App.css'
import { ThemeProvider } from './context/theme'
import ThemeBtn from './components/ThemeBtn'
import Card from './components/Card'

function App() {
  // State to manage the current theme mode (default: "light")
  const [themeMode, setThemeMode] = useState("light")

  // Function to switch to light theme
  const lightTheme = () => {
    setThemeMode("light")
  }

  // Function to switch to dark theme
  const darkTheme = () => {
    setThemeMode("dark")
  }

  // Effect to apply the theme class to the <html> element whenever themeMode changes
  useEffect(() => {
    // Remove any existing theme classes from <html>
    document.querySelector('html').classList.remove("light", "dark")
    // Add the current theme class to <html>
    document.querySelector('html').classList.add(themeMode)
  }, [themeMode])

  // Return the App component wrapped in ThemeProvider
  return (
    <ThemeProvider value={{ themeMode, lightTheme, darkTheme }}>
      <div className="flex flex-wrap min-h-screen items-center">
        <div className="w-full">
          <div className="w-full max-w-sm mx-auto flex justify-end mb-4">
            {/* Theme toggle button */}
            <ThemeBtn />
          </div>

          <div className="w-full max-w-sm mx-auto">
            {/* Card component */}
            <Card />
          </div>
        </div>
      </div>
    </ThemeProvider>
  )
}

export default App

Explanation of Syntax

  1. State Initialization:

     const [themeMode, setThemeMode] = useState("light")
    
    • useState creates a state variable (themeMode) and a setter function (setThemeMode).

    • Default value: "light".

  2. Theme Toggle Functions:

     const lightTheme = () => { setThemeMode("light") }
     const darkTheme = () => { setThemeMode("dark") }
    
    • These functions update the state to either "light" or "dark".
  3. useEffect:

     useEffect(() => {
       document.querySelector('html').classList.remove("light", "dark")
       document.querySelector('html').classList.add(themeMode)
     }, [themeMode])
    
    • Runs whenever themeMode changes.

    • Dynamically manages the classes on the <html> element to apply the selected theme.

  4. ThemeProvider:

     <ThemeProvider value={{ themeMode, lightTheme, darkTheme }}>
    
    • Wraps the app and provides context values (themeMode, lightTheme, darkTheme) to its children.
  5. Child Components:

     <ThemeBtn />
     <Card />
    
    • ThemeBtn: Likely interacts with theme functions to toggle themes.

    • Card: Represents a styled component.

  6. Layout:

     <div className="flex flex-wrap min-h-screen items-center">
    
    • Uses flexbox utilities for layout alignment.
  1. purpose:

    • Create a context (ThemeContext) to share theme-related state and methods across the app.
  2. Default Values for Context:

    • themeMode: Default theme is "light".

    • darkTheme and lightTheme: Placeholder methods for switching themes.

  3. ThemeProvider:

    • ThemeProvider is the context provider for ThemeContext.

    • It allows passing state and methods to any child components.

  4. Custom Hook:

    • useTheme: A custom hook to simplify access to ThemeContext.

    • Returns the current context value directly.

  5. No Need for Separate Files:

    • Combines context creation, provider, and hook in a single file for simplicity.

Complete Code (with comments)

import { useContext, createContext } from "react";

// Create a ThemeContext with default values
export const ThemeContext = createContext({
  themeMode: "light", // Default theme mode
  darkTheme: () => {}, // Placeholder function for dark theme
  lightTheme: () => {}, // Placeholder function for light theme
});

// Export ThemeProvider to wrap components
export const ThemeProvider = ThemeContext.Provider;

// Custom hook to access ThemeContext easily
export default function useTheme() {
  // Returns the value of ThemeContext
  return useContext(ThemeContext);
}
// No need for separate files; all theme-related logic is here.

Explanation of Syntax

  1. Creating the Context:

     export const ThemeContext = createContext({
       themeMode: "light",
       darkTheme: () => {},
       lightTheme: () => {},
     });
    
    • createContext: Creates a React context.

    • The object provided defines default values:

      • themeMode: "light" by default.

      • darkTheme and lightTheme: Placeholder methods (empty functions).

  2. ThemeProvider:

     export const ThemeProvider = ThemeContext.Provider;
    
    • Extracts the Provider from ThemeContext.

    • Used to wrap components needing access to the theme context.

  3. Custom Hook:

     export default function useTheme() {
       return useContext(ThemeContext);
     }
    
    • useContext: Retrieves the current value of ThemeContext.

    • Simplifies access to the context without importing useContext and the context separately.

  4. Combining Logic:

    • Context creation, provider, and custom hook are in one file for convenience.

Usage Example

In App.jsx, wrap the component tree with ThemeProvider and use useTheme in child components to access the context values

  • What is useTheme?

    • useTheme is a custom hook designed to make it easier to access the ThemeContext.

    • A custom hook is just a function that uses React hooks like useContext to simplify repetitive tasks.

  • Why use useContext?

    • Imagine you have a context, ThemeContext, which stores information about the app's theme (like "light" or "dark").

    • useContext is like a tool that lets you reach into the ThemeContext and grab its values (such as themeMode or methods like darkTheme and lightTheme).

  • Why not directly use useContext(ThemeContext) everywhere?

    • If every file or component in your app had to import ThemeContext and call useContext(ThemeContext) manually, it would be repetitive and messy.

    • Instead, by creating the useTheme custom hook, you:

      • Wrap the useContext logic in one place.

      • Make your components cleaner and easier to understand.

The useContext hook in React is used to consume a context created with React.createContext. It provides an easy way to access and share data across the component tree without the need to pass props manually at every level.

  1. Purpose:

    • ThemeBtn is a toggle button that allows users to switch between light and dark themes.
  2. Using useTheme:

    • The component uses the useTheme custom hook to access the current theme (themeMode) and methods (lightTheme and darkTheme) to change it.
  3. Handling Theme Changes:

    • When the toggle button is clicked, the onChangeBtn function determines whether to apply the dark or light theme based on the checkbox's state.
  4. Dynamic Checkbox State:

    • The checked property of the checkbox is dynamically set based on whether the current theme is dark.
  5. Styling:

    • The toggle button is styled using classes, with dynamic styles applied when the checkbox is checked or focused.

Complete Code (with comments)

import useTheme from "../context/theme"; // Import the custom hook to access the theme context

const ThemeBtn = () => {
  // Destructure the values from the useTheme hook
  const { themeMode, lightTheme, darkTheme } = useTheme();

  // Function to handle the toggle button's change
  const onChangeBtn = (e) => {
    // Get the current state of the checkbox
    const darkModeStatus = e.currentTarget.checked;

    // If checked, switch to dark theme; otherwise, switch to light theme
    if (darkModeStatus) {
      darkTheme(); // Call the darkTheme method
    } else {
      lightTheme(); // Call the lightTheme method
    }
  };

  // Return the toggle button JSX
  return (
    <label className="relative inline-flex items-center cursor-pointer">
      {/* Hidden checkbox input for the toggle functionality */}
      <input
        type="checkbox"
        value=""
        className="sr-only peer"
        onChange={onChangeBtn} // Handle the theme toggle
        checked={themeMode === "dark"} // Set the checkbox state based on the theme
      />
      {/* Styled toggle button */}
      <div className="w-11 h-6 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
      {/* Label text */}
      <span className="ml-3 text-sm font-medium text-gray-900">Toggle Theme</span>
    </label>
  );
};

export default ThemeBtn;

Explanation of Syntax

  1. Importing useTheme:

     import useTheme from "../context/theme";
    
    • Accesses the useTheme custom hook to get the theme state and methods.
  2. Destructuring Theme Values:

     const { themeMode, lightTheme, darkTheme } = useTheme();
    
    • themeMode: Current theme ("light" or "dark").

    • lightTheme: Method to switch to light mode.

    • darkTheme: Method to switch to dark mode.

  3. Handling Checkbox State:

     const onChangeBtn = (e) => {
       const darkModeStatus = e.currentTarget.checked;
       if (darkModeStatus) {
         darkTheme();
       } else {
         lightTheme();
       }
     };
    
    • Reads whether the checkbox is checked.

    • Calls darkTheme if checked; otherwise, calls lightTheme.

  4. Dynamic Checkbox Behavior:

     checked={themeMode === "dark"}
    
    • Automatically updates the checkbox state based on the current theme.
  5. Styled Toggle Button:

     <div className="..."></div>
    
    • Styled using Tailwind CSS classes for dynamic appearance when toggled or focused.