Hone logo
Hone
Problems

Implement a Custom useMediaQuery Hook in React

This challenge asks you to build a custom React hook, useMediaQuery, that allows your components to dynamically respond to browser viewport changes. This is crucial for creating responsive user interfaces that adapt their layout, styling, or behavior based on screen size.

Problem Description

You need to create a TypeScript React hook named useMediaQuery. This hook should accept a media query string (e.g., '(min-width: 768px)') as an argument and return a boolean value indicating whether the current browser window matches that media query.

Key Requirements:

  • The hook must accept a single argument: a string representing a valid CSS media query.
  • The hook must return a boolean: true if the media query matches the current viewport, false otherwise.
  • The hook must update its return value reactively when the viewport size changes.
  • The hook should leverage modern React features like useState and useEffect.

Expected Behavior:

When the hook is used in a component, the component should re-render whenever the media query's match status changes. For instance, if a component uses useMediaQuery('(min-width: 768px)') and the viewport is initially below 768px (returning false), it should re-render and update to true when the viewport is resized to be 768px or wider.

Edge Cases:

  • Initial State: The hook should correctly determine the initial match status of the provided media query when the component first mounts.
  • Invalid Media Query: While not strictly required to handle explicitly for this challenge, consider how a malformed media query might behave (though for this exercise, assume valid input).
  • Server-Side Rendering (SSR): Be mindful that window is not available on the server. The hook should gracefully handle this environment, likely by returning false by default during SSR.

Examples

Example 1: Basic Usage

// In your component:
import React from 'react';
import useMediaQuery from './useMediaQuery'; // Assuming your hook is in this file

function MyResponsiveComponent() {
  const isDesktop = useMediaQuery('(min-width: 1024px)');

  return (
    <div>
      {isDesktop ? (
        <p>You are viewing on a desktop!</p>
      ) : (
        <p>You are viewing on a smaller screen.</p>
      )}
    </div>
  );
}

Input to useMediaQuery: '(min-width: 1024px)'

Output (when viewport width >= 1024px): true

Output (when viewport width < 1024px): false

Explanation: The isDesktop variable will be true when the browser window is 1024px or wider, and false otherwise. The component will re-render to display the appropriate message.

Example 2: Combining Media Queries

// In your component:
import React from 'react';
import useMediaQuery from './useMediaQuery';

function NotificationBanner() {
  // Show banner only on mobile devices
  const isMobile = useMediaQuery('(max-width: 767px)');
  // And only if it's a specific condition, e.g., new feature available
  const isNewFeatureEnabled = true; // Assume this is fetched or set elsewhere

  const shouldShowBanner = isMobile && isNewFeatureEnabled;

  return (
    <div>
      {shouldShowBanner && (
        <div style={{ backgroundColor: 'lightblue', padding: '10px' }}>
          New feature available on mobile!
        </div>
      )}
    </div>
  );
}

Input to useMediaQuery: '(max-width: 767px)'

Output (when viewport width <= 767px and isNewFeatureEnabled is true): true

Output (when viewport width > 767px or isNewFeatureEnabled is false): false

Explanation: The shouldShowBanner variable will be true only if the viewport is 767px or narrower AND isNewFeatureEnabled is true. This demonstrates how the hook can be used to control UI elements based on viewport conditions.

Constraints

  • The hook must be implemented in TypeScript.
  • You should not use any third-party libraries for media query detection.
  • The hook should be performant and avoid unnecessary re-renders.

Notes

  • The window.matchMedia() API is your primary tool for this task.
  • Remember to set up event listeners for resize events to update the state when the viewport changes.
  • Ensure you clean up your event listeners when the component unmounts to prevent memory leaks.
  • Consider how to handle the initial state correctly, as window.matchMedia() can be called immediately.
Loading editor...
typescript