Skip to content

Avoid spurious re-renders in context providers#6180

Draft
masenf wants to merge 1 commit intomainfrom
masenf/avoid-theme-rerenders
Draft

Avoid spurious re-renders in context providers#6180
masenf wants to merge 1 commit intomainfrom
masenf/avoid-theme-rerenders

Conversation

@masenf
Copy link
Collaborator

@masenf masenf commented Mar 16, 2026

More aggressive memoization of theme provider and related components to avoid over re-rendering of components that depend on things like the resolvedColorMode.

Additionally add useMemo:

  • error boundary - prevent high level app-wrap re-renders
  • EventLoopProvider - prevents event loop re-rendering when the location/params/navigate hooks change

More aggressive memoization of theme provider and related components to avoid
over re-rendering of components that depend on things like the
resolvedColorMode.

Additionally add useMemo:
* error boundary - prevent high level app-wrap re-renders
* EventLoopProvider - prevents event loop re-rendering when the
  location/params/navigate hooks change
@codspeed-hq
Copy link

codspeed-hq bot commented Mar 16, 2026

Merging this PR will not alter performance

✅ 8 untouched benchmarks


Comparing masenf/avoid-theme-rerenders (865d2d7) with main (7607fa3)

Open in CodSpeed

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 16, 2026

Greptile Summary

This PR adds aggressive memoization across several React context providers and high-level components to prevent spurious re-renders that cascade through the component tree.

  • RadixThemesColorModeProvider: Wraps toggleColorMode and setColorMode in useCallback, and the entire provider return in useMemo, so children only re-render when color mode values actually change.
  • ThemeProvider (react-theme.js): Fixes a missing [] dependency array on the media query useEffect, which was previously running its setup/teardown on every render instead of only on mount.
  • EventLoopProvider (templates.py): Wraps the provider createElement in useMemo with [addEvents, connectErrors, children] deps, preventing re-renders when router hooks (useLocation, useNavigate, etc.) change but provider values remain stable.
  • Error Boundary (app.py): Wraps the default error boundary in a memo() call to isolate it from high-level app-wrap re-renders.
  • state.js: Adds useMemo to imports but it is unused in this file (minor nit).

Confidence Score: 4/5

  • This PR is safe to merge — the memoization changes are well-targeted and follow correct React patterns.
  • Score of 4 reflects that all core changes are correct React memoization patterns that should meaningfully reduce unnecessary re-renders. The only minor issue is an unused useMemo import in state.js. The useEffect dependency fix in react-theme.js is a clear bug fix. The useMemo/useCallback additions in the providers are standard React optimization patterns with correct dependency arrays.
  • reflex/.templates/web/utils/state.js has an unused useMemo import that should be removed.

Important Files Changed

Filename Overview
reflex/.templates/web/components/reflex/radix_themes_color_mode_provider.js Correctly applies useCallback to toggleColorMode/setColorMode and useMemo to the provider return value to prevent unnecessary re-renders of children depending on color mode context.
reflex/.templates/web/utils/react-theme.js Fixes a missing dependency array on the media query useEffect — previously running on every render, now correctly runs only on mount with empty deps.
reflex/.templates/web/utils/state.js Adds useMemo to imports but it is never used in this file — appears to be an accidental unused import.
reflex/app.py Wraps the default error boundary in a memo() call to prevent re-renders from high-level app-wrap changes propagating into the error boundary.
reflex/compiler/templates.py Wraps EventLoopProvider's createElement in useMemo to prevent re-rendering when location/params/navigate hooks change but addEvents/connectErrors remain stable.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[App Layout] --> B[ThemeProvider]
    B --> C[StateProvider]
    C --> D[EventLoopProvider]
    D --> E[AppWrap / ErrorBoundary]
    E --> F[Page Components]

    B -- "useEffect fix: media query listener<br/>runs only on mount now" --> B
    G[RadixThemesColorModeProvider] -- "useCallback: toggleColorMode, setColorMode<br/>useMemo: Provider createElement" --> G
    D -- "useMemo: Provider createElement<br/>deps: addEvents, connectErrors, children" --> D
    E -- "memo(): error boundary isolated<br/>from app-wrap re-renders" --> E

    style B fill:#d4edda,stroke:#28a745
    style D fill:#d4edda,stroke:#28a745
    style E fill:#d4edda,stroke:#28a745
    style G fill:#d4edda,stroke:#28a745
Loading

Last reviewed commit: 865d2d7

import reflexEnvironment from "$/reflex.json";
import Cookies from "universal-cookie";
import { useCallback, useEffect, useRef, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused useMemo import

useMemo was added to this import but is never used anywhere in state.js. The useMemo usage for EventLoopProvider lives in the generated context template (templates.py), not in this file. This will cause lint warnings for unused imports.

Suggested change
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useCallback, useEffect, useRef, useState } from "react";

@masenf masenf marked this pull request as draft March 16, 2026 19:33
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.

1 participant