S
SolidJS•3mo ago
DH

Losing reactivity when importing a signal from a library that uses SolidJS

Hey 👋 I have a library that uses SolidJS, and a few projects that import said library. I have the following code in one of my library's util files:
import { createEffect, createSignal } from "solid-js";
import Vec2 from "./data/Vec2";

export const [windowSize, setWindowSize] = createSignal<Vec2>(
Vec2.of(window.innerWidth, window.innerHeight),
);

createEffect(() => {
console.log("lib", windowSize());
});

createEffect(() => {
const onResize = () => setWindowSize(Vec2.of(window.innerWidth, window.innerHeight));
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
});
import { createEffect, createSignal } from "solid-js";
import Vec2 from "./data/Vec2";

export const [windowSize, setWindowSize] = createSignal<Vec2>(
Vec2.of(window.innerWidth, window.innerHeight),
);

createEffect(() => {
console.log("lib", windowSize());
});

createEffect(() => {
const onResize = () => setWindowSize(Vec2.of(window.innerWidth, window.innerHeight));
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
});
and the Vec2 class that I'm using (with all the other irrelevant methods removed, to keep the code snippet short):
export default class Vec2 {
public x: number;
public y: number;

private constructor(x: number, y: number) {
this.x = x;
this.y = y;
}

public static of(x: number, y: number): Vec2 {
return new Vec2(x, y);
}
}
export default class Vec2 {
public x: number;
public y: number;

private constructor(x: number, y: number) {
this.x = x;
this.y = y;
}

public static of(x: number, y: number): Vec2 {
return new Vec2(x, y);
}
}
The code above works as expected inside the library; When the window is resized, the signal updates, and the console.log effect gets called. The issue I have is inside another project, that imports my library. The effects there doesn't get called after the initial render. Here's my index.tsx entrypoint in the example project, it's more-or-less the default one when setting up a new SolidJS project:
import { render } from "solid-js/web";
import App from "./App";

const root = document.getElementById("root");

if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
);
}

render(() => <App />, root!);
import { render } from "solid-js/web";
import App from "./App";

const root = document.getElementById("root");

if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(
"Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?",
);
}

render(() => <App />, root!);
And the App.tsx:
import { type Component, createEffect } from "solid-js";
import { windowSize } from "nodeflow-lib";

const App: Component = () => {
createEffect(() => {
console.log("proj", windowSize());
});

return <h1>Test</h1>;
};

export default App;
import { type Component, createEffect } from "solid-js";
import { windowSize } from "nodeflow-lib";

const App: Component = () => {
createEffect(() => {
console.log("proj", windowSize());
});

return <h1>Test</h1>;
};

export default App;
Any hints on why the effect inside the example app only gets called once (on load), while the one inside the library works as intended?
No description
10 Replies
Eve
Eve•3mo ago
You're showing code, but you're not showing where, or how it's imported. Neither yourindex.tsx nor App.tsx appear to have any imports that relate to the code defined at the top.
DH
DHOP•3mo ago
I removed parts of the code that I thought were irrelevant to keep the messages shorter. I edited the original message with all the imports added. Vec2 has some more stuff, but none of it should relate to SolidJS. As for the exporting, give me a sec... in the library, in src/index.ts (the entrypoint), I'm exporting everything from my utils dir
export * from "./utils";
export * from "./utils";
and then in the src/utils/index.ts im exporting the screen-utils:
export * from "./screen-utils";
export * from "./screen-utils";
Madaxen86
Madaxen86•3mo ago
You can also use this primitive which helps to set up a resize observer https://primitives.solidjs.community/package/resize-observer#makeresizeobserver
Solid Primitives
A library of high-quality primitives that extend SolidJS reactivity
DH
DHOP•3mo ago
The full repo is available here, at least for the library. The example app I'm using is not on the repo: https://github.com/LordDeatHunter/nodeflow Thanks, I'll probably look into that, but I'm still curious as to what causes the reactivity issue in my scenario in the first place.
Eve
Eve•3mo ago
Well... the answer is, something outside of what you've posted.
Eve
Eve•3mo ago
Solid Playground
Quickly discover what the solid compiler will generate from your JSX template
Eve
Eve•3mo ago
Works just fine in the playground.
DH
DHOP•3mo ago
Yes, but you have all 3 files in the same project. It works fine if everything is in a single project, but that's not the case for me. In my case, I have the screen utils and Vec2 class in 1 library project, and I have that installed on a 2nd example app (the index.tsx and Apps.tsx files are here), which is where the problem occurs. @Emily here's a minimally reproducable version: https://codesandbox.io/p/devbox/practical-vaughan-lwjs4f the main directory is the library containing the screen size logic the example/ directory contains the currently running project first text is the imported signal 2nd line is a component that does the same, but inside the library
Eve
Eve•3mo ago
My best guess is that the bundling process is messing something up, since importing from a clone of the source repo works fine, and importing from the npm package doesn't. Exact same test code, just toggling the path of the import statement. Unfortunately that's a little beyond my knowledge to debug.
DH
DHOP•2mo ago
Alright, thanks anyway. I'll see if I can zero in on exactly what's causing the issue. hopefully its just a bundler misconfiguration and not an issue with solidjs I found a solution by adding the following to my build.rollupOptions in vite.config.ts:
rollupOptions: {
external: ["solid-js", "solid-js/web", "solid-js/store"],
output: {
exports: "named",
globals: {
"solid-js": "solid",
"solid-js/web": "solidWeb",
"solid-js/store": "solidStore",
}
},
},
rollupOptions: {
external: ["solid-js", "solid-js/web", "solid-js/store"],
output: {
exports: "named",
globals: {
"solid-js": "solid",
"solid-js/web": "solidWeb",
"solid-js/store": "solidStore",
}
},
},
Hopefully this can help someone else with the same troubles 🙂
Want results from more Discord servers?
Add your server