Repository: reduxjs/react-redux
Pull request #1452 (2019-11-07 12:43:58)

Revert "fix performance createListenerCollection (#1450)"
This reverts commit 476c0de5d20acd45fbcea09fe7e52304184cc274.

https://github.com/reduxjs/react-redux/pull/1450 There is no guarantee on the order of calling parent/child selectors.

netlify[bot]: Deploy preview for *react-redux-docs* ready!

Built with commit 7a4557b26f78d75318eddbc5c11c550b65e8219e

https://deploy-preview-1452--react-redux-docs.netlify.com

Patch:
diff --git a/src/utils/Subscription.js b/src/utils/Subscription.js
index 064102e..e03f483 100644
--- a/src/utils/Subscription.js
+++ b/src/utils/Subscription.js
@@ -4,36 +4,46 @@ import { getBatch } from './batch'
 // well as nesting subscriptions of descendant components, so that we can ensure the
 // ancestor components re-render before descendants
 
+const CLEARED = null
 const nullListeners = { notify() {} }
 
 function createListenerCollection() {
   const batch = getBatch()
-  let listeners = {}
-  let id = 0
+  // the current/next pattern is copied from redux's createStore code.
+  // TODO: refactor+expose that code to be reusable here?
+  let current = []
+  let next = []
 
   return {
     clear() {
-      listeners = {}
+      next = CLEARED
+      current = CLEARED
     },
 
     notify() {
+      const listeners = (current = next)
       batch(() => {
-        for (const id in listeners) {
-          listeners[id]()
+        for (let i = 0; i < listeners.length; i++) {
+          listeners[i]()
         }
       })
     },
 
     get() {
-      return listeners
+      return next
     },
 
     subscribe(listener) {
-      const currentId = id++
-      listeners[currentId] = listener
+      let isSubscribed = true
+      if (next === current) next = current.slice()
+      next.push(listener)
 
       return function unsubscribe() {
-        delete listeners[currentId]
+        if (!isSubscribed || current === CLEARED) return
+        isSubscribed = false
+
+        if (next === current) next = current.slice()
+        next.splice(next.indexOf(listener), 1)
       }
     }
   }


Test patch:
diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js
index 51fb765..b0c8692 100644
--- a/test/hooks/useSelector.spec.js
+++ b/test/hooks/useSelector.spec.js
@@ -97,11 +97,11 @@ describe('React', () => {
             </ProviderMock>
           )
 
-          expect(Object.keys(rootSubscription.listeners.get()).length).toBe(1)
+          expect(rootSubscription.listeners.get().length).toBe(1)
 
           store.dispatch({ type: '' })
 
-          expect(Object.keys(rootSubscription.listeners.get()).length).toBe(2)
+          expect(rootSubscription.listeners.get().length).toBe(2)
         })
 
         it('unsubscribes when the component is unmounted', () => {
@@ -125,11 +125,11 @@ describe('React', () => {
             </ProviderMock>
           )
 
-          expect(Object.keys(rootSubscription.listeners.get()).length).toBe(2)
+          expect(rootSubscription.listeners.get().length).toBe(2)
 
           store.dispatch({ type: '' })
 
-          expect(Object.keys(rootSubscription.listeners.get()).length).toBe(1)
+          expect(rootSubscription.listeners.get().length).toBe(1)
         })
 
         it('notices store updates between render and store subscription effect', () => {