Skip to content

asBaseComponent: stale module-level colorScheme prevents re-render on scheme switch back #3945

@restarajat

Description

@restarajat

Description

asBaseComponent HOC captures colorScheme as a module-level constant at import time. Since BaseComponent extends React.PureComponent (via UIComponent), components mounted after a scheme change hold a stale state.colorScheme that matches the original scheme. When switching back to that original scheme, the appearanceListener guard (this.state.colorScheme !== colorScheme) evaluates to false, skipping setState and preventing re-render.

Related to

  • Components
  • Demo
  • Docs
  • Typings

Steps to reproduce

  1. App starts in light mode → module-level colorScheme is captured as 'light'
  2. Go to a settings/appearance screen and switch to dark mode → all RNUI components re-render correctly
  3. Navigate to any screen — new components mount during dark mode with state.colorScheme = 'light' (stale module-level value)
  4. Switch back to light mode → Scheme.broadcastSchemeChange('light') fires
  5. Stale components check 'light' !== 'light'falsesetState is never called → no re-render

Expected behavior

All RNUI components (Text, View, Button, Switch, etc.) should re-render with the updated color scheme when switching in both directions (light → dark AND dark → light).

Actual behavior

Components mounted after the first scheme change do not re-render when switching back to the original scheme. They retain the previous scheme's colors (e.g., light gray text on a light background) because BaseComponent (a PureComponent) skips rendering — both props and state appear unchanged.

More Info

Code snippet

The bug is in src/commons/asBaseComponent.js:

// Line 8 — evaluated ONCE at module load, never updated
const colorScheme = Scheme.getSchemeType();

function asBaseComponent(WrappedComponent, options = {}) {
  // BaseComponent extends UIComponent which extends React.PureComponent
  class BaseComponent extends UIComponent {
    state = {
      error: false,
      colorScheme  // All instances get the stale module-level value
    };

    appearanceListener = colorScheme => {
      // This guard fails for stale instances when switching back to the original scheme
      if (this.state.colorScheme !== colorScheme) {
        this.setState({ colorScheme });
      }
    };
  }
}

Fix — evaluate Scheme.getSchemeType() per instance instead of once at module level:

 const EMPTY_MODIFIERS = {};
-const colorScheme = Scheme.getSchemeType();
 function asBaseComponent(WrappedComponent, options = {}) {
   class BaseComponent extends UIComponent {
     state = {
       error: false,
-      colorScheme
+      colorScheme: Scheme.getSchemeType()
     };

Screenshots/Video

N/A — the visual symptom is dark-theme text colors (e.g., #C9D6DF) rendered on a light-theme background (#F0F5F9), causing text to blend/disappear.

Environment

  • React Native version: 0.83
  • React Native UI Lib version: 8.3.4

Affected platforms

  • Android
  • iOS
  • Web

Metadata

Metadata

Assignees

No one assigned

    Labels

    buga bug in one of the components

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions