Skip to content

Reduce React effect store allocations - #977

Draft
JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/react-effect-store-memory
Draft

Reduce React effect store allocations#977
JoviDeCroock wants to merge 1 commit into
mainfrom
JoviDeCroock/react-effect-store-memory

Conversation

@JoviDeCroock

@JoviDeCroock JoviDeCroock commented Aug 29, 2026

Copy link
Copy Markdown
Member

Each mounted React component currently owns closures for every EffectStore method. Move the render-only methods to a shared prototype while keeping subscribe and getSnapshot as direct per-instance closures.

The hybrid shape avoids bound-call overhead, removes 224 B per mounted component, and adds 29 B gzip to runtime.mjs.

React EffectStore — 45.0% faster creation, 44.3% faster render cycle, 224 B smaller
import { effect } from "@preact/signals-core";

function HybridStore() {
  let effectInstance;
  let version = 0;
  let onChange;
  this._endEffect = undefined;

  const unsubscribe = effect(function () {
    effectInstance = this;
  });
  this.effect = effectInstance;
  effectInstance._callback = () => {
    version = (version + 1) | 0;
    onChange?.();
  };
  this.subscribe = function (fn) {
    onChange = fn;
    return () => {
      version = (version + 1) | 0;
      onChange = undefined;
      unsubscribe();
    };
  };
  this.getSnapshot = function () {
    return version;
  };
}
HybridStore.prototype._start = function () {
  this._endEffect = this.effect._start();
};
HybridStore.prototype.f = function () {
  const end = this._endEffect;
  this._endEffect = undefined;
  end?.();
};

const stores = Array.from({ length: 200_000 }, () => new HybridStore());
const start = performance.now();
for (const store of stores) {
  store._start();
  store.f();
}
console.log(performance.now() - start);

@changeset-bot

changeset-bot Bot commented Aug 29, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3ba2e2f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@preact/signals-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@netlify

netlify Bot commented Aug 29, 2026

Copy link
Copy Markdown

Deploy Preview for preact-signals-demo ready!

Name Link
🔨 Latest commit 3ba2e2f
🔍 Latest deploy log https://app.netlify.com/projects/preact-signals-demo/deploys/6a929328146998000893d6dd
😎 Deploy Preview https://deploy-preview-977--preact-signals-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: +2 B (0%)

Total Size: 199 kB

📦 View Changed
Filename Size Change
docs/dist/assets/client-********.js 46.6 kB +1 B (0%)
docs/dist/react-********.js 243 B +1 B (+0.41%)
ℹ️ View Unchanged
Filename Size
docs/dist/assets/bench-********.js 1.6 kB
docs/dist/assets/devtools-********.js 911 B
docs/dist/assets/EmbeddedDevtools-********.js 23 kB
docs/dist/assets/index-********.js 540 B
docs/dist/assets/jsxRuntime.module-********.js 300 B
docs/dist/assets/preact.module-********.js 4.74 kB
docs/dist/assets/signals-core.module-********.js 1.92 kB
docs/dist/assets/signals.module-********.js 2.66 kB
docs/dist/assets/style-********.css 7.19 kB
docs/dist/assets/Unmount-********.js 651 B
docs/dist/assets/utils.module-********.js 520 B
docs/dist/basic-********.js 247 B
docs/dist/nesting-********.js 1.15 kB
packages/core/dist/signals-core.js 1.94 kB
packages/core/dist/signals-core.mjs 1.93 kB
packages/debug/dist/debug.js 4.72 kB
packages/debug/dist/debug.mjs 4.24 kB
packages/devtools-adapter/dist/devtools-adapter.js 2.36 kB
packages/devtools-adapter/dist/devtools-adapter.mjs 2.07 kB
packages/devtools-ui/dist/devtools-ui.js 21.3 kB
packages/devtools-ui/dist/devtools-ui.mjs 20.6 kB
packages/preact-transform/dist/signals-transform.js 1.66 kB
packages/preact-transform/dist/signals-transform.mjs 1.61 kB
packages/preact-transform/dist/signals-transform.umd.js 1.77 kB
packages/preact/dist/signals.js 1.82 kB
packages/preact/dist/signals.mjs 1.76 kB
packages/react-transform/dist/signals-transform.js 7.63 kB
packages/react-transform/dist/signals-transform.mjs 6.78 kB
packages/react-transform/dist/signals-transform.umd.js 7.75 kB
packages/react/dist/signals.js 214 B
packages/react/dist/signals.mjs 165 B
packages/vite-plugin/dist/vite-plugin.js 8.86 kB
packages/vite-plugin/dist/vite-plugin.mjs 7.86 kB

compressed-size-action

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