Skip to content

Commit

Permalink
fix(store/enhancers): revert to use getStateWithSelectors
Browse files Browse the repository at this point in the history
  • Loading branch information
exuanbo committed Dec 14, 2023
1 parent d207330 commit 1b6a102
Show file tree
Hide file tree
Showing 6 changed files with 47 additions and 47 deletions.
17 changes: 17 additions & 0 deletions src/app/store/enhancers/getStateWithSelector.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { Selector, Store } from '@reduxjs/toolkit'

import { injectStoreExtension } from './injectStoreExtension'

interface GetStateWithSelector<State> {
(): State
<Selected>(selector: Selector<State, Selected>): Selected
}

export const createSelectorEnhancer = <State>() =>
injectStoreExtension<{ getState: GetStateWithSelector<State> }>((store: Store<State>) => {
const getState = <Selected>(selector?: Selector<State, Selected>) => {
const state = store.getState()
return selector ? selector(state) : state
}
return { getState }
})
12 changes: 7 additions & 5 deletions src/app/store/enhancers/injectStoreExtension.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import type { StoreEnhancer } from '@reduxjs/toolkit'
import type { Store, StoreEnhancer } from '@reduxjs/toolkit'

export const injectStoreExtension =
<StoreExtension extends {}>(extension: StoreExtension): StoreEnhancer<StoreExtension> =>
(createStore) =>
<StoreExtension extends {}>(
createExtension: <NextStore extends Store>(store: NextStore) => StoreExtension,
): StoreEnhancer<StoreExtension> =>
(next) =>
(...args) => {
const store = createStore(...args)
const store = next(...args)
return {
...store,
...extension,
...createExtension(store),
}
}
22 changes: 9 additions & 13 deletions src/app/store/enhancers/subscribeChange.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import type { Store, StoreEnhancer } from '@reduxjs/toolkit'
import type { Store } from '@reduxjs/toolkit'

type SubscribeChange = Store['subscribe']
import { injectStoreExtension } from './injectStoreExtension'

export const subscribeChange: StoreEnhancer<{ subscribeChange: SubscribeChange }> =
(createStore) =>
(...args) => {
const store = createStore(...args)
type SubscribeChange = Store['subscribe']

export const subscribeChange = injectStoreExtension<{ subscribeChange: SubscribeChange }>(
<State>(store: Store<State>) => {
let stateSnapshot: ReturnType<typeof store.getState> | null = null

const dispatch: typeof store.dispatch = (action) => {
Expand All @@ -18,17 +17,14 @@ export const subscribeChange: StoreEnhancer<{ subscribeChange: SubscribeChange }
}
}

const subscribeChange: typeof store.subscribe = (listener) =>
const subscribeChange: SubscribeChange = (listener) =>
store.subscribe(() => {
const state = store.getState()
if (state !== stateSnapshot) {
listener()
}
})

return {
...store,
dispatch,
subscribeChange,
}
}
return { dispatch, subscribeChange }
},
)
4 changes: 4 additions & 0 deletions src/app/store/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { exceptionSlice } from '@/features/exception/exceptionSlice'
import { ioSlice } from '@/features/io/ioSlice'
import { memorySlice } from '@/features/memory/memorySlice'

import { createSelectorEnhancer } from './enhancers/getStateWithSelector'
import { subscribeChange } from './enhancers/subscribeChange'
import { createActionObserver } from './observers/actionObserver'
import { createStateObserver } from './observers/stateObserver'
Expand All @@ -31,6 +32,8 @@ const rootReducer = combineSlices(

export type RootState = ReturnType<typeof rootReducer>

const getStateWithSelector = createSelectorEnhancer<RootState>()

const stateObserver = createStateObserver<RootState>()
const actionObserver = createActionObserver()

Expand All @@ -46,6 +49,7 @@ export const store = configureStore({
const defaultEnhancers = getDefaultEnhancers({ autoBatch: false })
return defaultEnhancers
.concat(subscribeChange)
.concat(getStateWithSelector)
.concat(stateObserver.enhancer, actionObserver.enhancer)
},
})
Expand Down
11 changes: 5 additions & 6 deletions src/app/store/observers/actionObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,12 @@ export const createActionObserver = (): ActionObserver => {
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getOrCache = createWeakCache<PayloadActionCreator<any>>()
const cache = createWeakCache<PayloadActionCreator<any>>()

const onAction: ObserveAction = (actionCreator) =>
getOrCache(actionCreator, () => action$.pipe(filter(matchType(actionCreator)), map(getPayload)))
cache(actionCreator, () => action$.pipe(filter(matchType(actionCreator)), map(getPayload)))

return {
middleware,
enhancer: injectStoreExtension({ onAction }),
}
const enhancer = injectStoreExtension(() => ({ onAction }))

return { middleware, enhancer }
}
28 changes: 5 additions & 23 deletions src/app/store/observers/stateObserver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,9 @@ import { createWeakCache } from './weakCache'

type ObserveState<State> = <Selected>(selector: Selector<State, Selected>) => Observable<Selected>

interface GetSelectedState<State> {
(): State
<Selected>(selector: Selector<State, Selected>): Selected
}

interface StateObserver<State> {
middleware: Middleware<{}, State>
enhancer: StoreEnhancer<{
onState: ObserveState<State>
getState: GetSelectedState<State>
}>
enhancer: StoreEnhancer<{ onState: ObserveState<State> }>
}

export const createStateObserver = <State extends {}>(): StateObserver<State> => {
Expand Down Expand Up @@ -47,22 +39,12 @@ export const createStateObserver = <State extends {}>(): StateObserver<State> =>
}
}

const getOrCache = createWeakCache<Selector<State, unknown>>()
const cache = createWeakCache<Selector<State, unknown>>()

const onState: ObserveState<State> = (selector) =>
getOrCache(selector, () => distinctState$.pipe(map(selector), distinctUntilChanged()))
cache(selector, () => distinctState$.pipe(map(selector), distinctUntilChanged()))

const getState: GetSelectedState<State> = <Selected>(selector?: Selector<State, Selected>) => {
const state = state$.getValue()
invariant(state != null)
return selector ? selector(state) : state
}
const enhancer = injectStoreExtension(() => ({ onState }))

return {
middleware,
enhancer: injectStoreExtension({
onState,
getState,
}),
}
return { middleware, enhancer }
}

0 comments on commit 1b6a102

Please sign in to comment.