Closed na47io closed 3 months ago
This is a configuration error, when using https://esm.sh, you must make sure you're marking Preact as external else you'll end up getting multiple copies -- Preact must be a singleton else hooks will not work, and signals rely upon hooks in order to function correctly. Here's an example with an import map, which is the recommended way to go about this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/preact@10.12.1",
"preact/hooks": "https://esm.sh/preact@10.12.1/hooks?external=preact",
"@preact/signals": "https://esm.sh/@preact/signals@1.1.3?external=preact",
"htm/preact": "https://esm.sh/htm@3.1.1/preact"
}
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module">
import { render } from 'preact';
import { computed, signal } from '@preact/signals';
import { useState } from 'preact/hooks';
import { html } from 'htm/preact';
// Create signals outside of the component
const count = signal(0);
const double = computed(() => count.value * 2);
export function App() {
const [hcount, setHcount] = useState(0);
const add = (n) => {
count.value += n;
console.log(count.value); // the state is updating
// UNCOMMENT LINE BELOW this to make signals work
// setHcount((c) => c + n);
};
return html`
<button onClick=${() => add(-1)}>Decrement</button>
<input readonly size="4" value=${count.value} />
<input readonly size="4" value=${double.value} />
<button onClick=${() => add(1)}>Increment</button>
`;
}
render(html`<${App} />`, document.getElementById('root'));
</script>
</body>
</html>
Oh I see...Every day is a school day!
Thank you @rschristian! ๐
No problem.
There's an open issue to add this information to our docs site (https://github.com/preactjs/preact-www/issues/1116); just out of curiosity, would that have helped? Did you go looking for that information on our website at all?
While I still need to get around to writing that page, I'm also trying to gauge where we might need to add links for it to catch as many users as possible.
I somewhat agree with https://github.com/preactjs/preact-www/issues/1116 description. When looking for a solution I saw a lot of people getting pwned by the singleton issue in different ways.
I came to preact
first from fresh and now for preact/signals
. I am new to the ecosystem and the way preact
scales down to my use-case without sacrificing ergonomics sparked a lot of joy. To me at least that was not immediately obvious from skimming https://preactjs.com/
One idea could be a No Build Tools Walkthrough that incrementally adds complexity:
single html file -> add jsx -> add typescript -> SSR it -> Island it (?) -> Resumability it (??)
A doc like that could be useful for a newcomer like me looking for a quote-unquote principled approach. Since I am on this journey myself I might write this.
I think changing the example code for https://preactjs.com/guide/v10/getting-started#no-build-tools-route slightly might get us most of the way.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Some App</title>
<script type="importmap">
{
"imports": {
"preact": "https://esm.sh/preact@10.12.1",
"preact/hooks": "https://esm.sh/preact@10.12.1/hooks?external=preact",
"@preact/signals": "https://esm.sh/@preact/signals@1.1.3?external=preact"
}
}
</script>
</head>
<body>
</body>
<script type="module">
import { h, render } from 'preact';
import { signal } from "@preact/signals";
const count = signal(0)
// Create your app
const app = () => {
return h('div', null, [
h(`h1`, null, `count: ${count.value}`),
h('button', {
onclick: () => {
count.value++;
}
}, "Increment")]
);
}
render(h(app), document.body);
</script>
</html>
I speculate this example can align better with user's needs: adding reactivity instead of rendering static markup. This code then:
h
@preact/signals
importMap
case that trips a lot of people.Then just move the NOTE from https://preactjs.com/guide/v10/getting-started#using-preact-with-htm-and-importmaps. And make it red ๐ .
This is a few more LOC, but imho introduces essential concepts in a no-build setting. Getting Started
will get a little busy and might need to link to sub-pages for the three cases.
That said, my issue would have resolved itself have I read the Getting Started section top-to-bottom as I should have ๐ค
If this sounds good I'm happy to pick up the issue.
One idea could be a No Build Tools Walkthrough that incrementally adds complexity:
single html file -> add jsx -> add typescript -> SSR it -> Island it (?) -> Resumability it (??)
Well you're not getting JSX, TS, or (automatic) islands without build tooling -- fundamentally they require it. Not sure I really understand this.
I speculate this example can align better with user's needs: adding reactivity instead of rendering static markup. This code then:
- introduces h
- shows off @preact/signals
- hits the importMap case that trips a lot of people.
I don't think this is really appropriate there, no. The point of that example is to show the bare-minimum you need to get up and running. We don't want to introduce something completely optional and unrelated like signals. The main point of import maps is to orchestrate dependencies of which that example has only 1, so it's largely unneeded IMO.
They definitely start being necessary when you add in hooks, signals, htm, etc., and those are the cases I want to document as they're a bit more advanced.
That said, my issue would have resolved itself have I read the Getting Started section top-to-bottom as I should have ๐ค
To be clear, I wasn't saying you should have read that! Even if you had, that section only touches on the idea, would be easy to miss and easy to incorrectly implement even if you guessed it was needed. Importmaps. while great, are not without headaches and are very fiddly and easy to mess up.
My question was more just seeing if docs there could have helped, as I want to make sure when they are added that they're as accessible as possible.
I do think a separate "Advanced" page is best, I want to avoid extending the "Getting Started" doc (as you mentioned) and keeping the examples there as minimal as possible -- that page really is meant to be "Here's how you can start writing Preact and set up aliases", nothing more. Components, hooks, signals, etc., can be left to other pages.
Really appreciate your feedback though, I'll try to get this on my todo list soon-ish.
Going to close this out, further discussion can happen at https://github.com/preactjs/preact-www/issues/1116 as this is a docs issue more than a signals issue.
Well you're not getting JSX, TS, or (automatic) islands without build tooling
every day really is a school day ๐
I look forward to the new docs! I can tell i need to read em...
TLDR: Lack of build tooling means you're left with standard JS and only standard JS -- JSX and TS are non-standard syntaxes, requiring transpilation before running, so they're out, and automatic islands require build tools to identify, wrap, and split out chunks as well as mount them on the client. Manual islands are still possible, but fiddly/easy to break and the DX ain't great.
Wrote this up, would love to know if you have any thoughts (if you have the time): https://deploy-preview-1175--preactjs.netlify.app/guide/v10/no-build-workflows
@rschristian that's brilliant! Great idea with the common patterns section!
Glad to hear it! It's linked from the 'Getting Started' docs, hopefully this will help in the future.
Environment
@preact/signals-core
@preact/signals
@preact/signals-react
describe the bug
When using
@preact/signals
the state updates but the re-render never happens.Adding
useState
frompreact/hooks
, specifically invoking thesetCounter
callback forces the re-render (?) and fixes the issue.to reproduce
See minimal example
```htmlsteps
Increment
button - observe nothing happeningclient.tsx:23
, repeat button mash - observe rerenderexpected behaviour
the component re-renders without having to
useState
.