Batch Updates
Microcosm will never fire a change
event if data is the same. However it is still possible to push a large number of actions at the same time, all of which modify data.
The problem is that each of these actions will trigger a change
event, which may cause a costly re-render. If an animation is playing, the user might see a degradation in frame rate.
An easy solution for this is simply to emit fewer change
events. To wait for a few actions to complete, then emit a single change
event at once. We call this “batching”.
The batch option
Microcosm provides a batch
option that tells Microcosm that it is okay to group multiple change events together. This reduces the number of change events, resulting in less rendering. For example, without batching, this code would fire 3 change events:
let repo = new Microcosm()
let add = (n) => n
repo.addDomain('count', {
getInitialState () {
return 0
},
register () {
return {
[add]: (count, n) => count + 1
}
}
})
repo.push(add, 2) // change!
repo.push(add, 2) // change!
repo.push(add, 2) // change!
With batching, only a single change event would fire:
let repo = new Microcosm({ batch: true })
// ...
repo.push(add, 2)
repo.push(add, 2)
repo.push(add, 2)
// change!
The updater option
Most of the time, batch: true
should be sufficient for accommodating apps with a high degree of change. However Microcosm provides an updater
option that allows for more specific customization of the update process. For example, if we wanted to batch updates to a 12 millisecond interval:
function updater () {
// update is a function, executing it will send a request to
// Microcosm, asking it trigger a change event if anything changed
return update => setTimeout(update, 12)
}
This will spool up any changes within a 12 millisecond time frame, sending out a larger update after the timeout completes.
Gotchas with testing
The problem with batching is that you have to wait, which is problematic for testing. For example:
import App from 'src/views/application'
import { mount } from 'enzyme'
test("it increases the number when the stepper is clicked", function () {
let app = mount(<App />)
app.find('#stepper').simulate('click')
let count = app.find('#count').text()
expect(count).toEqual('1')
})
The test above is synchronous. It expects the Microcosm associated with App
to update immediately. However, if we configure Microcosm to wait a few milliseconds before changing, this won’t have happened yet.
The easiest way to deal with this problem is to only pass batch: true
for user facing code. However you could also lean in repo.history.wait()
, which introduces a few more elements to the test:
import Repo from 'src/repo'
import App from 'src/views/application'
import { mount } from 'enzyme'
test("it increases the number when the stepper is clicked", async function () {
let repo = new Repo()
let app = mount(<App repo={repo} />)
app.find('#stepper').simulate('click')
// wait() will pause this test until all actions finish
await repo.history.wait()
let count = app.find('#count').text()
expect(count).toEqual('1')
})