Theme Switcher (context api)
Dependencies and Imports:
useEffect
anduseState
are React hooks.ThemeProvider
is imported from a custom context (./context/theme
).ThemeBtn
andCard
are components used withinApp
.
State Management:
- A
themeMode
state is initialized with a default value"light"
.
- A
Theme Switch Handlers:
lightTheme
: Sets thethemeMode
to"light"
.darkTheme
: Sets thethemeMode
to"dark"
.
Theme Application Using
useEffect
:Watches changes to
themeMode
.Dynamically applies the theme by adding/removing classes (
light
ordark
) to the<html>
tag.
Component Structure:
ThemeProvider
: Wraps the child components, providing them access tothemeMode
,lightTheme
, anddarkTheme
.ThemeBtn
: Button for toggling the theme.Card
: Example UI component (specific details not provided).
Layout:
- A basic flexbox layout is used to center-align the
ThemeBtn
andCard
components.
- A basic flexbox layout is used to center-align the
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
State Initialization:
const [themeMode, setThemeMode] = useState("light")
useState
creates a state variable (themeMode
) and a setter function (setThemeMode
).Default value:
"light"
.
Theme Toggle Functions:
const lightTheme = () => { setThemeMode("light") } const darkTheme = () => { setThemeMode("dark") }
- These functions update the state to either
"light"
or"dark"
.
- These functions update the state to either
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.
ThemeProvider:
<ThemeProvider value={{ themeMode, lightTheme, darkTheme }}>
- Wraps the app and provides context values (
themeMode
,lightTheme
,darkTheme
) to its children.
- Wraps the app and provides context values (
Child Components:
<ThemeBtn /> <Card />
ThemeBtn
: Likely interacts with theme functions to toggle themes.Card
: Represents a styled component.
Layout:
<div className="flex flex-wrap min-h-screen items-center">
- Uses flexbox utilities for layout alignment.
purpose:
- Create a context (
ThemeContext
) to share theme-related state and methods across the app.
- Create a context (
Default Values for Context:
themeMode
: Default theme is"light"
.darkTheme
andlightTheme
: Placeholder methods for switching themes.
ThemeProvider:
ThemeProvider
is the context provider forThemeContext
.It allows passing state and methods to any child components.
Custom Hook:
useTheme
: A custom hook to simplify access toThemeContext
.Returns the current context value directly.
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
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
andlightTheme
: Placeholder methods (empty functions).
ThemeProvider:
export const ThemeProvider = ThemeContext.Provider;
Extracts the
Provider
fromThemeContext
.Used to wrap components needing access to the theme context.
Custom Hook:
export default function useTheme() { return useContext(ThemeContext); }
useContext
: Retrieves the current value ofThemeContext
.Simplifies access to the context without importing
useContext
and the context separately.
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 theThemeContext
.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 theThemeContext
and grab its values (such asthemeMode
or methods likedarkTheme
andlightTheme
).
Why not directly use
useContext(ThemeContext)
everywhere?If every file or component in your app had to import
ThemeContext
and calluseContext(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.
Purpose:
ThemeBtn
is a toggle button that allows users to switch between light and dark themes.
Using
useTheme
:- The component uses the
useTheme
custom hook to access the current theme (themeMode
) and methods (lightTheme
anddarkTheme
) to change it.
- The component uses the
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.
- When the toggle button is clicked, the
Dynamic Checkbox State:
- The
checked
property of the checkbox is dynamically set based on whether the current theme is dark.
- The
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
Importing
useTheme
:import useTheme from "../context/theme";
- Accesses the
useTheme
custom hook to get the theme state and methods.
- Accesses the
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.
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, callslightTheme
.
Dynamic Checkbox Behavior:
checked={themeMode === "dark"}
- Automatically updates the checkbox state based on the current theme.
Styled Toggle Button:
<div className="..."></div>
- Styled using Tailwind CSS classes for dynamic appearance when toggled or focused.