useToggle
Simple React hook for managing boolean state with convenient toggle functions for common operations.
The useToggle
hook provides an easy way to manage boolean state with additional utility functions for common operations like toggling, setting true/false explicitly. It's perfect for modals, feature flags, loading states, and any boolean-based UI interactions.
Basic Usage
import { useToggle } from 'light-hooks';
function Modal() {
const { value: isOpen, toggle, setTrue: open, setFalse: close } = useToggle(false);
return (
<div>
<button onClick={open}>Open Modal</button>
{isOpen && (
<div className="modal">
<div className="modal-content">
<h2>Modal Content</h2>
<p>This is a modal dialog.</p>
<button onClick={close}>Close</button>
<button onClick={toggle}>Toggle</button>
</div>
</div>
)}
</div>
);
}
API Reference
Parameters
Parameter | Type | Description |
---|---|---|
initialValue | boolean | The initial boolean value (optional, defaults to false ) |
Return Value
interface UseToggleReturn {
value: boolean; // Current boolean value
toggle: () => void; // Function to toggle the value
setTrue: () => void; // Function to set the value to true
setFalse: () => void; // Function to set the value to false
setValue: (value: boolean) => void; // Function to set a specific value
}
Examples
Feature Toggles and Settings
function Settings() {
const { value: darkMode, toggle: toggleDarkMode } = useToggle(false);
const { value: notifications, setValue: setNotifications } = useToggle(true);
const { value: autoSave, setTrue: enableAutoSave, setFalse: disableAutoSave } = useToggle(true);
return (
<div className="settings">
<h3>Application Settings</h3>
<div className="setting-item">
<label>
<input
type="checkbox"
checked={darkMode}
onChange={toggleDarkMode}
/>
Dark Mode
</label>
</div>
<div className="setting-item">
<label>
<input
type="checkbox"
checked={notifications}
onChange={(e) => setNotifications(e.target.checked)}
/>
Enable Notifications
</label>
</div>
<div className="setting-item">
<label>Auto Save</label>
<div>
<button
onClick={enableAutoSave}
className={autoSave ? 'active' : ''}
>
On
</button>
<button
onClick={disableAutoSave}
className={!autoSave ? 'active' : ''}
>
Off
</button>
</div>
</div>
</div>
);
}
Loading States
function DataComponent() {
const [data, setData] = useState(null);
const { value: loading, setTrue: startLoading, setFalse: stopLoading } = useToggle(false);
const { value: error, setTrue: showError, setFalse: hideError } = useToggle(false);
const fetchData = async () => {
startLoading();
hideError();
try {
const response = await fetch('/api/data');
if (!response.ok) throw new Error('Failed to fetch');
const result = await response.json();
setData(result);
} catch (err) {
showError();
console.error('Fetch error:', err);
} finally {
stopLoading();
}
};
return (
<div>
<button onClick={fetchData} disabled={loading}>
{loading ? 'Loading...' : 'Fetch Data'}
</button>
{error && (
<div className="error">
<p>Failed to load data</p>
<button onClick={hideError}>Dismiss</button>
</div>
)}
{data && (
<div className="data">
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
)}
</div>
);
}
Sidebar and Navigation
function Layout({ children }: { children: React.ReactNode }) {
const { value: sidebarOpen, toggle: toggleSidebar, setFalse: closeSidebar } = useToggle(false);
const { value: mobileMenuOpen, toggle: toggleMobileMenu } = useToggle(false);
return (
<div className="layout">
<header>
<button onClick={toggleSidebar} className="sidebar-toggle">
β° Menu
</button>
<button onClick={toggleMobileMenu} className="mobile-menu-toggle">
π± Mobile Menu
</button>
</header>
<div className="layout-content">
{sidebarOpen && (
<aside className="sidebar">
<nav>
<a href="/" onClick={closeSidebar}>Home</a>
<a href="/about" onClick={closeSidebar}>About</a>
<a href="/contact" onClick={closeSidebar}>Contact</a>
</nav>
<button onClick={closeSidebar}>Close Sidebar</button>
</aside>
)}
<main className="main-content">
{children}
</main>
</div>
{mobileMenuOpen && (
<div className="mobile-menu-overlay">
<div className="mobile-menu">
<button onClick={toggleMobileMenu}>β Close</button>
<nav>
<a href="/">Home</a>
<a href="/about">About</a>
<a href="/contact">Contact</a>
</nav>
</div>
</div>
)}
</div>
);
}
Form Validation and States
function ContactForm() {
const [formData, setFormData] = useState({ name: '', email: '', message: '' });
const { value: isSubmitting, setTrue: startSubmitting, setFalse: stopSubmitting } = useToggle(false);
const { value: showSuccess, setTrue: showSuccessMessage, setFalse: hideSuccess } = useToggle(false);
const { value: showValidation, setTrue: enableValidation } = useToggle(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
enableValidation();
if (!formData.name || !formData.email) {
return; // Show validation errors
}
startSubmitting();
try {
await submitForm(formData);
showSuccessMessage();
setFormData({ name: '', email: '', message: '' });
// Hide success message after 3 seconds
setTimeout(hideSuccess, 3000);
} catch (error) {
console.error('Submit error:', error);
} finally {
stopSubmitting();
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
placeholder="Name"
value={formData.name}
onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
/>
{showValidation && !formData.name && (
<span className="error">Name is required</span>
)}
</div>
<div>
<input
type="email"
placeholder="Email"
value={formData.email}
onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
/>
{showValidation && !formData.email && (
<span className="error">Email is required</span>
)}
</div>
<textarea
placeholder="Message"
value={formData.message}
onChange={(e) => setFormData(prev => ({ ...prev, message: e.target.value }))}
/>
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? 'Sending...' : 'Send Message'}
</button>
{showSuccess && (
<div className="success">
Message sent successfully!
</div>
)}
</form>
);
}
Advanced Examples
Accordion Component
function AccordionItem({ title, children }: { title: string; children: React.ReactNode }) {
const { value: isExpanded, toggle } = useToggle(false);
return (
<div className="accordion-item">
<button
className="accordion-header"
onClick={toggle}
aria-expanded={isExpanded}
>
{title}
<span className={`arrow ${isExpanded ? 'expanded' : ''}`}>βΌ</span>
</button>
{isExpanded && (
<div className="accordion-content">
{children}
</div>
)}
</div>
);
}
function FAQ() {
return (
<div className="accordion">
<AccordionItem title="How do I get started?">
<p>You can get started by installing our package and reading the documentation.</p>
</AccordionItem>
<AccordionItem title="Is it free to use?">
<p>Yes, our library is completely free and open source.</p>
</AccordionItem>
<AccordionItem title="Can I contribute?">
<p>Absolutely! We welcome contributions from the community.</p>
</AccordionItem>
</div>
);
}
Multi-Step Form
function MultiStepForm() {
const { value: step1Complete, setTrue: completeStep1 } = useToggle(false);
const { value: step2Complete, setTrue: completeStep2 } = useToggle(false);
const { value: showReview, setTrue: showReviewStep } = useToggle(false);
const [formData, setFormData] = useState({
personal: { name: '', email: '' },
preferences: { newsletter: false, notifications: true },
review: { terms: false }
});
const handleStep1Submit = (data: any) => {
setFormData(prev => ({ ...prev, personal: data }));
completeStep1();
};
const handleStep2Submit = (data: any) => {
setFormData(prev => ({ ...prev, preferences: data }));
completeStep2();
showReviewStep();
};
return (
<div className="multi-step-form">
<div className="progress">
<div className={`step ${step1Complete ? 'complete' : 'active'}`}>
1. Personal Info
</div>
<div className={`step ${step2Complete ? 'complete' : step1Complete ? 'active' : ''}`}>
2. Preferences
</div>
<div className={`step ${showReview ? 'active' : ''}`}>
3. Review
</div>
</div>
{!step1Complete && (
<PersonalInfoStep onSubmit={handleStep1Submit} />
)}
{step1Complete && !step2Complete && (
<PreferencesStep onSubmit={handleStep2Submit} />
)}
{showReview && (
<ReviewStep data={formData} />
)}
</div>
);
}
Feature Flags System
function FeatureFlagsProvider({ children }: { children: React.ReactNode }) {
const { value: betaFeatures, toggle: toggleBetaFeatures } = useToggle(false);
const { value: experimentalUI, toggle: toggleExperimentalUI } = useToggle(false);
const { value: advancedMode, setValue: setAdvancedMode } = useToggle(false);
const features = {
betaFeatures,
experimentalUI,
advancedMode,
toggleBetaFeatures,
toggleExperimentalUI,
setAdvancedMode
};
return (
<FeatureFlagsContext.Provider value={features}>
{children}
</FeatureFlagsContext.Provider>
);
}
function FeatureFlagsPanel() {
const {
betaFeatures,
experimentalUI,
advancedMode,
toggleBetaFeatures,
toggleExperimentalUI,
setAdvancedMode
} = useContext(FeatureFlagsContext);
return (
<div className="feature-flags-panel">
<h3>Feature Flags</h3>
<label>
<input
type="checkbox"
checked={betaFeatures}
onChange={toggleBetaFeatures}
/>
Enable Beta Features
</label>
<label>
<input
type="checkbox"
checked={experimentalUI}
onChange={toggleExperimentalUI}
/>
Experimental UI
</label>
<div>
<span>Advanced Mode:</span>
<button onClick={() => setAdvancedMode(true)}>Enable</button>
<button onClick={() => setAdvancedMode(false)}>Disable</button>
</div>
</div>
);
}
Theme Switcher
function ThemeSwitcher() {
const { value: isDark, toggle: toggleTheme, setValue: setTheme } = useToggle(false);
const { value: isSystemTheme, toggle: toggleSystemTheme } = useToggle(false);
useEffect(() => {
if (isSystemTheme) {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
setTheme(mediaQuery.matches);
const handler = (e: MediaQueryListEvent) => setTheme(e.matches);
mediaQuery.addEventListener('change', handler);
return () => mediaQuery.removeEventListener('change', handler);
}
}, [isSystemTheme, setTheme]);
useEffect(() => {
document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');
}, [isDark]);
return (
<div className="theme-switcher">
<h3>Theme Settings</h3>
<label>
<input
type="checkbox"
checked={isSystemTheme}
onChange={toggleSystemTheme}
/>
Use System Theme
</label>
{!isSystemTheme && (
<div className="manual-theme-controls">
<button
onClick={() => setTheme(false)}
className={!isDark ? 'active' : ''}
>
βοΈ Light
</button>
<button
onClick={() => setTheme(true)}
className={isDark ? 'active' : ''}
>
π Dark
</button>
<button onClick={toggleTheme}>
π Toggle
</button>
</div>
)}
<p>Current theme: {isDark ? 'Dark' : 'Light'}</p>
</div>
);
}
Comparison with useState
While you could achieve similar functionality with useState
, useToggle
provides convenience methods:
// Using useState (more verbose)
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(prev => !prev);
const open = () => setIsOpen(true);
const close = () => setIsOpen(false);
// Using useToggle (concise)
const { value: isOpen, toggle, setTrue: open, setFalse: close } = useToggle(false);
TypeScript Support
The hook is fully typed with comprehensive TypeScript interfaces:
const {
value, // boolean
toggle, // () => void
setTrue, // () => void
setFalse, // () => void
setValue // (value: boolean) => void
}: UseToggleReturn = useToggle(false);
Performance Tips
- Destructure with meaningful names: Use descriptive aliases for better code readability
- Memoize callbacks: If passing toggle functions as props, consider using
useCallback
- Combine related toggles: Group related boolean states in a single component
Common Use Cases
- π UI States: Modals, dropdowns, sidebars, menus
- βοΈ Settings: Feature toggles, preferences, configurations
- π Forms: Validation states, submission states, field visibility
- π¨ Themes: Dark/light mode, UI variants
- π± Responsive: Mobile menu states, collapsible content
- π Loading: Request states, progress indicators
- π― Features: A/B testing, feature flags, experimental features
Best Practices
- Use descriptive aliases:
const { value: isVisible, setTrue: show, setFalse: hide }
- Group related toggles: Keep related boolean states in the same component
- Initialize with meaningful defaults: Consider the most common initial state
- Combine with other hooks: Works well with
useEffect
,useLocalStorage
, etc. - Handle cleanup: Remember to clean up listeners or timers in effects
Perfect for any boolean state management in your React applications!