I recently took on a new project and since I was following solid.js for a while, I thought I'd give it a try. And to reduce the time needed to develop the project I decided to port components from another project I built previously, using preact.
I replaced useState
with createSignal
or createStore
, useEffect
with createEffect
, etc., with their appropriate syntax (no dependencies for createEffect
etc). Since solid shares a lot with react, I thought these fixes were enough. Boy was I surprised. I'll list my findings below. I might be wrong on some details so feel free to correct me.
Solid doesn't like custom hooks exported as default
I had a custom hook to show a loader, whose state is controlled in a context.
// loader.jsx
export default function useLoading() {
...
return () => {...}
}
// component.jsx
import useLoading from './loader.jsx'
...
const loading = useLoading()
loading(true)
...
loading(false)
And the loader doesn't show. Then I copied over the hook definition to component.jsx
and it worked. So I tried converting it to a named export, and it worked.
Using state outside of returned JSX is not reactive.
I think this is because components only run once, but
it took me hours to get this:
...
return state.loading && <Loader />
What do you think happens when state.loading
changes? Nothing. All changes must be inside the JSX for the component to be reactive.
...
return <>
{state.loading && <Loader />}
</>
Using For
as a top-level return value with a changing list creates an infinite loop
Even when the For
is inside a top-level fragment.
...
return <For each={state.list}>
{...}
</For>
Or
return <>
<For each={state.list}>
{...}
</For>
</>
Both of these create an infinite loop. It must be at least one level deep.
return <div>
<For each={state.list}>
{...}
</For></div>
</div>
As I said in the beginning, all of these may be due to my wrong understanding, but they did take a lot of hours for me to find, and I wanted to share them here. Thanks for reading.
Top comments (4)
Thanks for the feedback. Only the second issue is actually by design for Solid.
The first issue is a limit to component identification heuristic for Hot Module Loading found in the Vite starter(github.com/solidjs/solid-refresh). We need to do better here. It's basically thinks the hook is a component when you put it as the default export in a .tsx/.jsx file. This is good to point out and will be improved in the future.
I am not aware of the 3rd issue. It sounds like a bug. Maybe also due to Hot Module Loading. I've never seen but maybe just by chance but works fine in things like the playground: https://playground.solidjs.com/?hash=1947242128&version=1.2.5. Feel free to report any bugs you find on github or drop them in the discord.
Thank you Ryan, that clears it up. I'll investigate the third point further, under dev server and built. Thanks for the feedback!
I believe this is incorrect. Changes must be inside a reactive context for them to be reactive. JSX works, but I think the following does as well:
I had some trouble with this at first as well. Someone (might have been Ryan) pointed out to me that, in React, everything in a component re-runs on every change unless you wrap it in a context that prevents it from re-running (e.g. put code inside
useMemo()
). In Solid, nothing in a component re-runs unless you wrap it in a context that tells it to re-run (e.g. by usingcreateMemo()
or by using a signal inside JSX).I can confirm that is is false. Also a call to a memo MUST be inside a JSX-Component.