diff --git a/package-lock.json b/package-lock.json index c1271a1121..c3d308dacb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "redux", - "version": "4.0.4", + "version": "4.0.5", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 86229c06f9..389df218b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "redux", - "version": "4.0.4", + "version": "4.0.5", "description": "Predictable state container for JavaScript apps", "license": "MIT", "homepage": "http://redux.js.org", diff --git a/src/createStore.ts b/src/createStore.ts index 3a980bba37..731f93dc98 100644 --- a/src/createStore.ts +++ b/src/createStore.ts @@ -103,6 +103,8 @@ export default function createStore< let currentListeners: (() => void)[] | null = [] let nextListeners = currentListeners let isDispatching = false + var isNotifyListenersNextFrame = false + var alreadyNotifingListenersOnNextFrame = false /** * This makes a shallow copy of currentListeners so we can use @@ -197,6 +199,18 @@ export default function createStore< } } + /** + * + * @param {boolean} shouldNotifyOnNextFrame A boolean to configure the store + * to notify the listeners only on the next frame or after every dispatch. + * The state still keep changing between dispatches, only listeners (subscribers to store) don't run. + * Set to true if listeners should be notified only once per frame. + * Set to false (default) if listeners should be notified after every dispatch + */ + function setNotifyListenersOnNextFrame(shouldNotifyOnNextFrame) { + isNotifyListenersNextFrame = shouldNotifyOnNextFrame + } + /** * Dispatches an action. It is the only way to trigger a state change. * @@ -248,10 +262,24 @@ export default function createStore< isDispatching = false } - const listeners = (currentListeners = nextListeners) - for (let i = 0; i < listeners.length; i++) { - const listener = listeners[i] - listener() + function notifyListeners() { + const listeners = (currentListeners = nextListeners) + for (let i = 0; i < listeners.length; i++) { + const listener = listeners[i] + listener() + } + } + + if (isNotifyListenersNextFrame) { + if (!alreadyNotifingListenersOnNextFrame) { + alreadyNotifingListenersOnNextFrame = true + window.requestAnimationFrame(() => { + notifyListeners() + alreadyNotifingListenersOnNextFrame = false + }) + } + } else { + notifyListeners() } return action @@ -345,6 +373,7 @@ export default function createStore< subscribe, getState, replaceReducer, + setNotifyListenersOnNextFrame, [$$observable]: observable } as unknown) as Store, A, StateExt, Ext> & Ext return store