Implementing useReducedMotion Hook in React for Accessibility
The useReducedMotion hook is a valuable tool for improving the accessibility of React applications, particularly for users with vestibular disorders or those who prefer reduced motion. This hook detects the user's system preference for reduced motion (via prefers-reduced-motion media query) and provides a boolean value indicating whether animations should be disabled. This challenge asks you to implement this hook in TypeScript.
Problem Description
You are tasked with creating a custom React hook called useReducedMotion. This hook should:
- Detect System Preference: Utilize the
prefers-reduced-motionmedia query to determine the user's system preference for reduced motion. - Return a Boolean Value: Return a boolean value indicating whether reduced motion is preferred by the user.
truesignifies that reduced motion is preferred, andfalsesignifies that animations are acceptable. - React to Changes: The hook should re-render whenever the user's preference changes. This ensures that your application dynamically adapts to the user's settings.
- Handle Initial Load: The hook should provide a sensible initial value while the media query is being evaluated. A default of
false(animations enabled) is acceptable.
Expected Behavior:
- When the user's system preference is set to
reduce, the hook should returntrue. - When the user's system preference is set to
no-preferenceorany, the hook should returnfalse. - The hook should re-render whenever the
prefers-reduced-motionmedia query changes.
Edge Cases to Consider:
- Initial Load: The media query might not be immediately available on initial load. The hook should handle this gracefully and provide a default value.
- Browser Support: While
prefers-reduced-motionis widely supported, consider that older browsers might not support it. The hook should still function without errors in such cases (returningfalseis a reasonable fallback).
Examples
Example 1:
Input: User's system preference is set to "reduce".
Output: true
Explanation: The hook detects the "reduce" preference and returns true, indicating that animations should be disabled.
Example 2:
Input: User's system preference is set to "no-preference".
Output: false
Explanation: The hook detects the "no-preference" preference and returns false, indicating that animations are acceptable.
Example 3:
Input: Initial load of the application before the media query is evaluated.
Output: false
Explanation: The hook returns a default value of false (animations enabled) until the media query is evaluated.
Constraints
- Language: TypeScript
- React Version: Compatible with React 18 or later.
- Performance: The hook should be performant and avoid unnecessary re-renders. Use
useLayoutEffectjudiciously if needed, but prioritizeuseEffectfor this scenario. - Dependencies: The solution should minimize external dependencies. Only use built-in React APIs.
Notes
- You can use
matchMediafrom thewindowobject to access theprefers-reduced-motionmedia query. - Consider using a functional component and the
useStatehook to manage the state of the preference. - Think about how to handle the initial load and the asynchronous nature of media query evaluation.
- The goal is to create a reusable hook that can be easily integrated into any React component to control animations based on user preference.