diff --git a/src/store.ts b/src/store.ts index f76c8bacb..a9a75c0ba 100644 --- a/src/store.ts +++ b/src/store.ts @@ -36,13 +36,23 @@ export class StateStore> { }; }; - public subscribeWithSelector = (selector: (nextValue: T) => O, handler: Handler) => { + public subscribeWithSelector = > & { length?: never }>( + selector: (nextValue: T) => O, + handler: Handler, + ) => { // begin with undefined to reduce amount of selector calls let selectedValues: O | undefined; const wrappedHandler: Handler = (nextValue) => { const newlySelectedValues = selector(nextValue); - const hasUpdatedValues = selectedValues?.some((value, index) => value !== newlySelectedValues[index]) ?? true; + + let hasUpdatedValues = !selectedValues; + + for (const key in selectedValues) { + if (selectedValues[key] === newlySelectedValues[key]) continue; + hasUpdatedValues = true; + break; + } if (!hasUpdatedValues) return; diff --git a/src/thread.ts b/src/thread.ts index 9ea26141f..13874da67 100644 --- a/src/thread.ts +++ b/src/thread.ts @@ -197,8 +197,11 @@ export class Thread { private subscribeMarkActiveThreadRead = () => { return this.state.subscribeWithSelector( - (nextValue) => [nextValue.active, ownUnreadCountSelector(this.client.userID)(nextValue)], - ([active, unreadMessageCount]) => { + (nextValue) => ({ + active: nextValue.active, + unreadMessageCount: ownUnreadCountSelector(this.client.userID)(nextValue), + }), + ({ active, unreadMessageCount }) => { if (!active || !unreadMessageCount) return; this.throttledMarkAsRead(); }, @@ -207,8 +210,8 @@ export class Thread { private subscribeReloadActiveStaleThread = () => this.state.subscribeWithSelector( - (nextValue) => [nextValue.active, nextValue.isStateStale], - ([active, isStateStale]) => { + (nextValue) => ({ active: nextValue.active, isStateStale: nextValue.isStateStale }), + ({ active, isStateStale }) => { if (active && isStateStale) { this.reload(); } diff --git a/src/thread_manager.ts b/src/thread_manager.ts index 3d8d47976..3808676f1 100644 --- a/src/thread_manager.ts +++ b/src/thread_manager.ts @@ -119,9 +119,9 @@ export class ThreadManager { private subscribeManageThreadSubscriptions = () => this.state.subscribeWithSelector( - (nextValue) => [nextValue.threads] as const, - ([nextThreads], prev) => { - const [prevThreads = []] = prev ?? []; + (nextValue) => ({ threads: nextValue.threads } as const), + ({ threads: nextThreads }, prev) => { + const { threads: prevThreads = [] } = prev ?? {}; // Thread instance was removed if there's no thread with the given id at all, // or it was replaced with a new instance const removedThreads = prevThreads.filter((thread) => thread !== this.threadsById[thread.id]); @@ -133,8 +133,8 @@ export class ThreadManager { private subscribeReloadOnActivation = () => this.state.subscribeWithSelector( - (nextValue) => [nextValue.active], - ([active]) => { + (nextValue) => ({ active: nextValue.active }), + ({ active }) => { if (active) this.reload(); }, ); diff --git a/src/types.ts b/src/types.ts index 99fd747ef..c59215442 100644 --- a/src/types.ts +++ b/src/types.ts @@ -963,10 +963,10 @@ export type CreateChannelOptions = { diff --git a/test/unit/threads.test.ts b/test/unit/threads.test.ts index 4b70be557..36473a2c1 100644 --- a/test/unit/threads.test.ts +++ b/test/unit/threads.test.ts @@ -1322,14 +1322,17 @@ describe('Threads 2.0', () => { }, })); const spy = sinon.spy(); - threadManager.state.subscribeWithSelector((nextValue) => [nextValue.pagination.isLoadingNext], spy); + threadManager.state.subscribeWithSelector( + (nextValue) => ({ isLoadingNext: nextValue.pagination.isLoadingNext }), + spy, + ); spy.resetHistory(); await threadManager.loadNextPage(); expect(spy.callCount).to.equal(2); - expect(spy.firstCall.calledWith([true])).to.be.true; - expect(spy.lastCall.calledWith([false])).to.be.true; + expect(spy.firstCall.calledWith({ isLoadingNext: true })).to.be.true; + expect(spy.lastCall.calledWith({ isLoadingNext: false })).to.be.true; }); it('updates thread list and pagination', async () => {