So I'm going to start with the the premise that _Hot Module Replacement™_ should be as easy and pluggable as https://github.com/substack/react-starter-hmr, with minimal change to the client application.

This is a rough-guide on what is required to get it set up:
npm i --save-dev browserify-hmr udwatchify client.js -p browserify-hmr ...ud.defn(...) annotation to the part of the application to show which bits of the program are suitable for hot-reload (per screenshot above)My trouble is that with step 4. I couldn't find a way with choo to force a re-render of the page's current state like in the React example. I got close in two different ways - forcing a reboot of the entire app (so reverting to initial state) or views updating the _next_ time they were rendered (not insta-matically).
If anyone knows the secret to getting this set up, please tell! Otherwise if a change is required in choo to facilitate this, let's figure it out? I think it's going to be something like exposing a way to call render/re-render, unless I'm missing something!
Excited for this. This is probably a hacky idea, but the page should be re-rendered any time a reducer is fired, regardless of whether the state was updated. So you could inject a reducer that simply returns the current state, thereby not changing it but re-rendering the page anyway. But I'm sure there's a more elegant way if we change something in choo.
Something magic (read: something I haven't figured out yet 😁 ) causes the React example to re-render, even though it seems to just be updating the class/prototype of the <App /> component.
@timwis I'm going to give your suggestion of a noop-y reducer a try and see if that sorts us out!
Ok, brace yo'selves… here's how HMR is achievable in choo right now:
npm install --save-dev browserify-hmr ud-p browserify-hmr to your browserify optionsud.defn() call like so:module.exports = ud.defn(module, (state, prev, send) => {
return html`
<div>Blah</div>
`
})
(here's the nastiest bit!)
noop reducer and a subscription that repeatedly calls it: ...
reducers: {
noop: (action, state) => ({})
},
subscriptions: [
(send, done) => {
setInterval(() => send('noop', done), 1000)
}
]
...
So I'm going to try to distill all of this down to what I think are the two functional requirements needed to get a decent dev set up…
ud.defn(...) needs to be added to the codebaseThe final piece of the puzzle is knowing when to call 2.. I'm still baffled by how the React one works. Does React use a render/digest loop? As far as I can see, browserify-hmr nor ud provide "events", but I'm sure we could hook into them somehow. The former logs a message to the console when it hot-swaps a module in.
Noting down @yoshuawuyts hot reload proof of concept here https://github.com/yoshuawuyts/hot-app-replacement/tree/choo
I think this is interesting but as far as I can see it
?
I think it would be cool if we could leverage these community projects and also benefit from actual hot-swaps of updated modules, rather than sending the entire bundle. I can see the benefit of the entire reload, if any views have any state that's not stored in a model obviously this would help.
I'd favor the approach in hot-app-replacement because:
Hah yeah so that's about it - obviously you're free to do what you want,
but this is my take on it (:
On Wed, Oct 26, 2016 at 6:59 PM Ben Gourley [email protected]
wrote:
Noting down @yoshuawuyts https://github.com/yoshuawuyts hot reload
proof of concept here
https://github.com/yoshuawuyts/hot-app-replacement/tree/chooI think this is interesting but as far as I can see it
- saves app state
- reloads the entire bundle
- loads app state
- re-boots the entire app
?
I think it would be cool if we could leverage these community projects and
also benefit from actual hot-swaps of updated modules, rather than sending
the entire bundle. I can see the benefit of the entire reload, if any views
have any state that's not stored in a model obviously this would help.—
You are receiving this because you were mentioned.Reply to this email directly, view it on GitHub
https://github.com/yoshuawuyts/choo/issues/295#issuecomment-256412122,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ACWlevJdm_Fim_PYDb7PCOyMZA_EJYznks5q34bUgaJpZM4KhBw_
.
@yoshuawuyts ok you have me convinced! Can we put together a list of todos in order to have this packaged up and ready to go?
I guess the only one reliant on you is the merge/publish of new barracks version? I'm happy to work on some of the other bits if we define what they are.
@bengourley
choo (choo-hmr perhaps?)bankai to use the server implementationSomething like this?
Sounds good @yoshuawuyts :D I might be able to get started on choo-mhr this week. It's going to be super useful to me on a project I'm working on.
[Edit: I hit cmd enter too soon and posted this before it was finished 😬 ]
So I've come up with a solution! Anyone want to try this out?
npm i --save choo-resume hot-rld
Attach the choo plugin. This saves the state on the window, and wraps the initial state to load from the window if it exists.
const app = require('choo')()
const resume = require('choo-resume')
app.start('#app-root')
Run the standalone SSE server. This watches the -b path on disk and notifies the client to hot-swap the <script> tag with source equal to -s.
./node_modules/.bin/hot-rld -b static/js/bundle.js -s /static/js/bundle.js
In your server rendered html output the client that connects to the SSE server.
const client = require('hot-rld/client')
const html = `<script>${client()}</script>`
Since this reloads the entire app bundle, any side effects of the app, including timers, event listeners and other subscription-like behaviour will happen multiple times.
Crude example, causes a duplicate interval to be set up every reload:
subscriptions: [
(send, done) => setInverval(() => console.log('boop'), 5000)
]
I can't think of a good way of solving this, other than sticking the state in local storage and doing a full page reload. But then it's not really a hot-reload, more like a snapshot/resume tool (_aside, that's not very hip and trendy, but it's still a useful and workable solution, perhaps we should run with that?_). This is why my gut feel previously was to go the hot-module-swap because then you can just swap out the immutable parts of choo and be guaranteed to not have side effects.
Thoughts❔❔❔
@bengourley oohhhh really digging this; might just work on top of this from https://github.com/yoshuawuyts/bankai/pull/86 - use the same client, but use bankai as the server. :grin:
Is there a way we could make this also work for CSS ? perhaps have different push channels that set the script and css individually? If we document the wire protocol then we could have compatible client and servers and like be super happy with all of it :sparkles:
@yoshuawuyts I decoupled the cli/standalone server from the sse route handler, so if you pull in require('hot-rld/server')(bundlePath, sourcePath) you get a fn with signature (req, res) => {} which you can mount wherever you like on a pre-existing server. You then just need to provide the address argument to the client script generator client('http://myhost:1234/path-to-where-sse-was-mounted')
As far as CSS is concerned, yeah sure we could namespace the sse events into channels. FYI I don't know if you dug deep into the code, but I'm only sending tiny payloads (i.e. the path of the file that needs replacing, not the source of the bundle) and the browser is re-requesting the entire bundle from wherever it was served initially.
I've published v1 of both https://github.com/bengourley/choo-resume and https://github.com/bengourley/hot-rld.
@yoshuawuyts hot-rld now supports css too.
Module reloading is part of bankai now too, couple with https://github.com/yoshuawuyts/choo-reload for SSE based reloading :sparkles:
Most helpful comment
I've published v1 of both https://github.com/bengourley/choo-resume and https://github.com/bengourley/hot-rld.
@yoshuawuyts hot-rld now supports css too.