Skip to content

Commit

Permalink
Clear memoized for state while defering notifications
Browse files Browse the repository at this point in the history
  • Loading branch information
ShirShintel committed Nov 3, 2024
1 parent 256a311 commit a04c04e
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 4 deletions.
6 changes: 5 additions & 1 deletion src/appHost.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ export function createAppHost(initialEntryPointsOrPackages: EntryPointOrPackage[
let isInstallingEntryPoints: boolean = false
let isStoreSubscribersNotifyInProgress = false
let isObserversNotifyInProgress = false
let isDeferringNotifications = false
const entryPointsInstallationEndCallbacks: Map<string, () => void> = new Map()

verifyLayersUniqueness(options.layers)
Expand Down Expand Up @@ -665,6 +666,9 @@ miss: ${memoizedWithMissHit.miss}
},
notifyObserversIsRunning => {
isObserversNotifyInProgress = notifyObserversIsRunning
},
deferNotifications => {
isDeferringNotifications = deferNotifications
}
)
store.subscribe(() => {
Expand All @@ -675,7 +679,7 @@ miss: ${memoizedWithMissHit.miss}
})
store.syncSubscribe(() => {
shouldFlushMemoization = true
if (isStoreSubscribersNotifyInProgress || isObserversNotifyInProgress) {
if (isStoreSubscribersNotifyInProgress || isObserversNotifyInProgress || isDeferringNotifications) {
shouldFlushMemoization = false
flushMemoizedForState()
}
Expand Down
9 changes: 6 additions & 3 deletions src/throttledStore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export interface StateContribution<TState = {}, TAction extends AnyAction = AnyA
export interface ThrottledStore<T = any> extends Store<T> {
hasPendingSubscribers(): boolean
flush(config?: { excecutionType: 'scheduled' | 'immediate' | 'default' }): void
deferSubscriberNotifications<K>(action: () => K | Promise<K>): Promise<K>
deferSubscriberNotifications<K>(action: () => K | Promise<K>, shouldClearMemoizedForState?: boolean): Promise<K>
}

export interface PrivateThrottledStore<T = any> extends ThrottledStore<T> {
Expand Down Expand Up @@ -157,7 +157,8 @@ export const createThrottledStore = (
requestAnimationFrame: Window['requestAnimationFrame'],
cancelAnimationFrame: Window['cancelAnimationFrame'],
updateIsSubscriptionNotifyInProgress: (isSubscriptionNotifyInProgress: boolean) => void,
updateIsObserversNotifyInProgress: (isObserversNotifyInProgress: boolean) => void
updateIsObserversNotifyInProgress: (isObserversNotifyInProgress: boolean) => void,
updateIsDeferringNotifications: (isDeferringNotifications: boolean) => void
): PrivateThrottledStore => {
let pendingBroadcastNotification = false
let pendingObservableNotifications: Set<AnyPrivateObservableState> | undefined
Expand Down Expand Up @@ -279,17 +280,19 @@ export const createThrottledStore = (
observableNotify: onObservableNotify,
resetPendingNotifications: resetAllPendingNotifications,
hasPendingSubscribers: () => pendingBroadcastNotification,
deferSubscriberNotifications: async action => {
deferSubscriberNotifications: async (action, shouldClearMemoizedForState) => {
if (isDeferrringNotifications) {
return action()
}
try {
executePendingActions()
isDeferrringNotifications = true
shouldClearMemoizedForState && updateIsDeferringNotifications(isDeferrringNotifications)
const functionResult = await action()
return functionResult
} finally {
isDeferrringNotifications = false
shouldClearMemoizedForState && updateIsDeferringNotifications(isDeferrringNotifications)
executePendingActions()
}
}
Expand Down
14 changes: 14 additions & 0 deletions test/connectWithShell.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,20 @@ describe('connectWithShell-useCases', () => {
expect(renderSpy).toHaveBeenCalledTimes(2)
})

it('should clear state memoization on every state update when deferring subscriber notifications', async () => {
const { host, shell } = createMocks(entryPointWithState, [entryPointSecondStateWithAPI])
let numberOfCalls = 0
const originalFn = jest.fn(() => ++numberOfCalls)
const memoizedFn = shell.memoizeForState(originalFn, () => '*') as _.MemoizedFunction
const clearCacheSpy = jest.spyOn(memoizedFn.cache, 'clear')

await host.getStore().deferSubscriberNotifications(() => {
host.getStore().dispatch({ type: 'MOCK' })
}, true)

expect(clearCacheSpy).toHaveBeenCalledTimes(1)
})

it('should not mount connected component on props update', () => {
const { host, shell, renderInShellContext } = createMocks(entryPointWithState, [entryPointSecondStateWithAPI])
const ConnectedComp = connectWithShell(mapStateToProps, undefined, shell, { allowOutOfEntryPoint: true })(PureComp)
Expand Down

0 comments on commit a04c04e

Please sign in to comment.