Hot module reloading is super cool. You can change your code in an instant, and you don't have to reload the page to see the result.
The most annoying part about it is that it throws away your application state! So if you want to develop with your app in a certain state, you have to recreate that state manually each time the code is reloaded.
cycle-restart
solves that problem for you! It records all the actions you perform and replays them after you change your code. Best part is that it happens in the blink of an eye!
edge/cyc is a great boilerplate project to help you get started with cycle-restart.
$ npm install cycle-restart --save-dev
cycle-restart
is designed to be used with hot module reloading, provided either by browserify-hmr or Webpack.
You'll want to set up your entry point (usually index.js
) like so:
import {setup} from '@cycle/run';
import {makeDOMDriver} from '@cycle/dom';
import {makeHTTPDriver} from '@cycle/http';
import {rerunner, restartable} from 'cycle-restart';
import app from './src/app';
const makeDrivers = () => ({
DOM: restartable(makeDOMDriver('.app'), {pauseSinksWhileReplaying: false}),
HTTP: restartable(makeHTTPDriver())
});
let rerun = rerunner(setup, makeDrivers);
rerun(app);
if (module.hot) {
module.hot.accept('./src/app', () => {
const newApp = require('./src/app').default;
rerun(newApp);
});
}
state
refers to the object containing the sinks and sources returned by Cycle.setup
.
Runs a new main
and drivers
, and replays any available history recorded on state
. Drivers will only be replayed if they are restartable.
If you use isolate
in your application, you should supply it so that its internal counter can be reset.
You can also supply a Javascript Date as destinationTime
if you wish to replay actions only up to a certain time.
Wraps driver
with the ability to record a log of actions and restart. When restart
is called, actions recorded on the driver's associated sources are replayed onto the driver.
If pauseSinksWhileReplaying
is true
, drivers will drop any items emitted during replay. Currently, this needs to be false
for the DOM driver.
Takes Cycle.setup
and a function that returns your drivers and produces a function that can be called with the same main
and destinationTime
as restart
, but automatically passes isolate
and the previous state
.
The rerun
pattern is an abstraction of the common use case where state
is saved and passed to the next restart
call, and isolate
does not change. rerun
can also be used instead of Cycle.run
to start the application for the first time.
cycle-restart
works great with browserify-hmr.
Assuming we have an index.js
with the above code, and src/app.js
exporting our main
function. We also need an index.html
with a <script>
that loads our bundle.js
First, we need to install watchify
and browserify-hmr
:
$ npm install watchify browserify-hmr babelify --save-dev
Then we can use watchify
to serve our bundle:
$ watchify -t babelify -p browserify-hmr index.js -o bundle.js
You can also use budo
as a development server. budo
makes it easy to also live reload your css. For an example of this, see Widdershin/cycle-hot-reloading-example
Have a look at the webpack docs for setting up hot module reloading.
The minimum requirement to get HMR working with webpack config is first to add these two entry points to your config.entry
entry: [
// The script refreshing the browser on none hot updates
'webpack-dev-server/client?http://localhost:8080',
'webpack/hot/dev-server', // For hot style updates
mainPath, // your actual entry
]
and of course, the HMR plugin itself in config.plugins
plugins: [new Webpack.HotModuleReplacementPlugin()]
Finally, run the command on the directory where your webpack config file is located. Defaults to webpack.config.js
webpack-dev-server --progress --colors --inline
For an example, look at https://github.com/FeliciousX/cyclejs-starter
(If anyone who has experience with SystemJS could add some instructions about how to get set up, it would be greatly appreciated)
cycle-restart
is tested against and known to work with the following drivers:
- cycle-dom
- cycle-http
- cycle-jsonp
- cycle-history
- cycle-animation-driver
Other drivers are likely to work off the bat. If you encounter a problem, please raise an issue and we will try and add compatability. If you use a driver not on this list and it works, let us know about it.
cycle-restart
does in fact support isolate. If you use isolate
in your apps, simply pass it as an extra argument to restart
.
import {setup} from '@cycle/run';
import {makeDOMDriver} from '@cycle/dom';
import {makeHTTPDriver} from '@cycle/http';
+ import isolate from '@cycle/isolate';
import {rerunner, restartable} from 'cycle-restart';
import app from './src/app';
const makeDrivers = () => ({
DOM: restartable(makeDOMDriver('.app'), {pauseSinksWhileReplaying: false}),
HTTP: restartable(makeHTTPDriver())
});
- const rerun = rerunner(setup, makeDrivers);
+ const rerun = rerunner(setup, makeDrivers, isolate);
rerun(app);
if (module.hot) {
module.hot.accept('./src/app', () => {
const newApp = require('./src/app').default;
rerun(newApp);
});
}
cycle-restart
relies on your main
function being pure. That means all real world side effects need to be encapsulated in drivers.
Here are some of the things that are likely to break right now:
- Accessing time without using a time driver such as
cycle-animation-driver
orcycle-time-driver
. Time is a side effect. (This includesRx.Observable.interval()
andRx.Observable.timestamp()
). Math.random()
. If you're using random numbers in your program, you should use a generator that produces deterministic numbers such as the mersenne twister from random-js.
Your contribution is extremely welcome. Please feel free to open issues, pull requests or help in any way you'd like. If you're unsure how best you can contribute, get in touch and we can chat.
cycle-restart
is released under the MIT license. Please see the LICENSE
file for full text.