diff --git a/src/alt/index.js b/src/alt/index.js index 51bf3613..752bf9d3 100644 --- a/src/alt/index.js +++ b/src/alt/index.js @@ -14,6 +14,7 @@ class Alt { this.serialize = config.serialize || JSON.stringify this.deserialize = config.deserialize || JSON.parse this.dispatcher = config.dispatcher || new Dispatcher() + this.batchingFunction = config.batchingFunction || (callback => callback()) this.actions = { global: {} } this.stores = {} this.storeTransforms = config.storeTransforms || [] @@ -23,7 +24,7 @@ class Alt { } dispatch(action, data, details) { - this.dispatcher.dispatch({ action, data, details }) + this.batchingFunction(() => this.dispatcher.dispatch({ action, data, details })) } createUnsavedStore(StoreModel, ...args) { diff --git a/test/batching-test.js b/test/batching-test.js new file mode 100644 index 00000000..d088817a --- /dev/null +++ b/test/batching-test.js @@ -0,0 +1,112 @@ +import { jsdom } from 'jsdom' +import Alt from '../' +import React from 'react/addons' +import { assert } from 'chai' +import sinon from 'sinon' + +const { TestUtils } = React.addons + +const Actions = { + buttonClick() { + setTimeout(() => { + this.actions.switchComponent() + }, 10) + }, + + switchComponent() { + this.dispatch() + }, + + uhoh() { + this.dispatch() + } +} + +function Store(actions) { + this.active = false + + this.bindAction(actions.switchComponent, () => { + this.active = true + }) +} + +class ComponentA extends React.Component { + constructor(props) { + super(props) + + this.state = props.alt.stores.store.getState() + } + + componentWillMount() { + this.props.alt.stores.store.listen(state => this.setState(state)) + } + + render() { + if (this.state.active) { + return + } else { + return
+ } + } +} + +class ComponentB extends React.Component { + componentWillMount() { + let error = null + try { + this.props.alt.actions.actions.uhoh() + } catch (err) { + error = err + } finally { + this.props.callback(error) + } + } + + render() { + return
+ } +} + +export default { + 'Batching dispatcher': { + beforeEach() { + global.document = jsdom('') + global.window = global.document.parentWindow + global.navigator = global.window.navigator + }, + + afterEach() { + delete global.document + delete global.window + delete global.navigator + }, + + 'does not batch'(done) { + const alt = new Alt() + alt.addActions('actions', Actions) + alt.addStore('store', Store, alt.actions.actions) + + function test(err) { + assert.match(err, /dispatch in the middle of a dispatch/) + done() + } + + TestUtils.renderIntoDocument() + alt.actions.actions.buttonClick() + }, + + 'allows batching'(done) { + const alt = new Alt({ batchingFunction: React.addons.batchedUpdates }) + alt.addActions('actions', Actions) + alt.addStore('store', Store, alt.actions.actions) + + function test(err) { + assert.isNull(err) + done() + } + + TestUtils.renderIntoDocument() + alt.actions.actions.buttonClick() + }, + } +}