Skip to content

Commit

Permalink
Addresses #959 without affecting the library's existing functionality…
Browse files Browse the repository at this point in the history
…. Ultimately tries to prevent re-renders when multiple states are needed in quick succession.
  • Loading branch information
robertleeplummerjr committed Jun 16, 2016
1 parent 4b63737 commit 3824a21
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 26 deletions.
62 changes: 36 additions & 26 deletions src/createStore.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
}

/**
* Dispatches an action. It is the only way to trigger a state change.
* Dispatches action(s). It is the only way to trigger a state change.
*
* The `reducer` function, used to create the store, will be called with the
* current state tree and the given `action`. Its return value will
Expand All @@ -139,45 +139,55 @@ export default function createStore(reducer, preloadedState, enhancer) {
* a good idea to keep actions serializable so you can record and replay user
* sessions, or use the time travelling `redux-devtools`. An action must have
* a `type` property which may not be `undefined`. It is a good idea to use
* string constants for action types.
* string constants for action types. Can handle multiple dispatches so to
* keep re-renders at a minimum.
*
* @returns {Object} For convenience, the same action object you dispatched.
* @returns {Object|Array} For convenience, the same action object you dispatched.
* If multiple actions are supplied, then will be returned as an Array.
*
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
* return something else (for example, a Promise you can await).
*/
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
function dispatch() {
var actions = []
var i;
for (i = 0; i < arguments.length; i++) {
var action = arguments[i]

if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}

if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}

if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}

try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}

try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
actions.push(action)
}

var listeners = currentListeners = nextListeners
for (var i = 0; i < listeners.length; i++) {
for (i = 0; i < listeners.length; i++) {
listeners[i]()
}

return action
return actions.length > 1 ? actions : actions[0]
}

/**
Expand Down
14 changes: 14 additions & 0 deletions test/createStore.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,20 @@ describe('createStore', () => {
])
})

it('can handle multiple dispatches', () => {
const store = createStore(reducers.todos)
store.dispatch(addTodo('Hello'), addTodo('World'))
expect(store.getState()).toEqual([
{
id: 1,
text: 'Hello'
}, {
id: 2,
text: 'World'
}
])
})

it('applies the reducer to the initial state', () => {
const store = createStore(reducers.todos, [
{
Expand Down

0 comments on commit 3824a21

Please sign in to comment.