Skip to content

Improved performance for useMediaQuery#435

Open
Profesor08 wants to merge 2 commits intosiberiacancode:mainfrom
Profesor08:useMediaQuery-improve-performance
Open

Improved performance for useMediaQuery#435
Profesor08 wants to merge 2 commits intosiberiacancode:mainfrom
Profesor08:useMediaQuery-improve-performance

Conversation

@Profesor08
Copy link

Optimize useMediaQuery, add shared stores and single listener per query

Current implementation has some problems

Every useMediaQuery instance maintain's its own MediaQueryList and change listener. With multiple components subscribing to the same query this meant:

  • window.matchMedia(query) called on every getSnapshot, allocating and discarding a new MediaQueryList on every render
  • N independent change listeners for N components on the same query, each triggering its own React re-render rather than a single batched update

Solution

A module-level Map cache now stores a single MediaQueryListStore per unique query string, shared across all subscribers.

  • Single window.matchMedia call per unique query - the instance is retained and reused by both subscribe and getSnapshot
  • Single change listener per unique query - attached on first subscriber, removed on last, regardless of how many components consume it
  • Stable getSnapshot - reads from the retained MediaQueryList instance instead of creating a throwaway object each call

@Profesor08 Profesor08 changed the title useMediaQuery-improve-performance 🧊 improved performance for useMedia… Improved performance for useMediaQuery Mar 1, 2026
@Profesor08 Profesor08 changed the title Improved performance for useMediaQuery Improved performance for useMediaQuery Mar 1, 2026
@debabin
Copy link
Member

debabin commented Mar 2, 2026

Hi @Profesor08 ! Thank you for your PR! Great job analyzing the problem and proposing optimizations for useMediaQuery.

I completely understand your desire to improve performance by eliminating multiple matchMedia calls and redundant subscriptions. You're absolutely right — the current implementation isn't ideal, and the problems you described are real.

But here's my main thought — I want to keep the library as simple as possible. Such optimizations aren't necessary for every project, and not every user needs them. However, I really like your idea.

That's why I'm thinking about creating a createSharedHook utility. This approach gives us two important benefits:

We don't need to rewrite every hook manually — once we create the utility, we can use it where optimizations are really needed

Developers can choose — those who need maximum performance can use hooks built with createSharedHook, while others can stick with the simple implementation

So instead of merging the PR right now, I suggest we:

  1. Create an internal createSharedHook utility
  2. Add example in doc

Your contribution is very valuable — it highlighted an important problem and pushed us to think about a universal solution. Thanks for helping improve the library!

@debabin
Copy link
Member

debabin commented Mar 3, 2026

export const useSharedMediaQuery = createSharedHook(useMediaQuery);
console.log("@", useSharedMediaQuery);

const First = () => {
  const matches = useSharedMediaQuery("(max-width: 768px)");
  return (
    <div>
      This is <code>{matches ? "mobile" : "desktop"}</code> screen
    </div>
  );
};
const Second = () => {
  const matches = useSharedMediaQuery("(max-width: 768px)");
  return (
    <div>
      This is <code>{matches ? "mobile" : "desktop"}</code> screen
    </div>
  );
};

const Demo = () => {
  return (
    <div>
      <First />
      <Second />
    </div>
  );
};

Prototype already done, i need fix some dx moments and add test

@debabin
Copy link
Member

debabin commented Mar 3, 2026

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants