Skip to content

Wrap subscribe callback with withMemCache in createStateManagementMixin #1396

Description

@tomerlichtash

Context

In packages/libs/sdk-mixins/src/mixins/createStateManagementMixin.ts, the subscribe method invokes the consumer callback on every store change:

this.subscribe = (cb, selector = (state) => state as any) =>
  store.subscribe(() => cb(selector(store.getState())));

As a result, every call site that only wants to react when the selected slice actually changes has to wrap its own callback with withMemCache from @descope/sdk-helpers. This pattern is repeated across many widget mixins, for example:

  • packages/widgets/audit-management-widget/src/lib/widget/mixins/initMixin/initComponentsMixins/initAuditTableMixin.ts
  • packages/widgets/user-profile-widget/src/lib/widget/mixins/initMixin/initComponentsMixins/initUserBuiltinAttributesMixin.ts
  • packages/widgets/user-profile-widget/src/lib/widget/mixins/initMixin/initComponentsMixins/initNotificationsMixin.ts
  • packages/widgets/user-profile-widget/src/lib/widget/mixins/initMixin/initComponentsMixins/initEmailUserAttrMixin.ts
  • ... and many more under packages/widgets/**/initComponentsMixins/

Typical pattern at each call site:

#onAuditListUpdate = withMemCache((auditList) => { ... });

// ...
this.subscribe(this.#onAuditListUpdate.bind(this), getAuditList);

Proposal

Move the withMemCache wrapping inside subscribe itself, so the callback only fires when the selector's output actually changes:

this.subscribe = (cb, selector = (state) => state as any) => {
  const memoized = withMemCache(cb);
  return store.subscribe(() => memoized(selector(store.getState())));
};

Then remove the redundant withMemCache(...) wrappers from every subscribe call site across the widgets.

Benefits

  • Removes duplicated boilerplate across all widget mixins.
  • Centralizes the change-detection behavior — easier to reason about and harder to forget.
  • Reduces the surface area for bugs where someone forgets to wrap their callback and triggers unnecessary re-renders/DOM work.

Scope

  • Update createStateManagementMixin.ts to wrap cb with withMemCache.
  • Audit all .subscribe( callers in packages/widgets/** (and elsewhere) and drop the now-redundant withMemCache wrapping.
  • Verify no caller relies on the callback firing on every store change regardless of selector output.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions