Skip to content

Commit

Permalink
refactor: use RxJS
Browse files Browse the repository at this point in the history
  • Loading branch information
exuanbo committed Aug 13, 2023
1 parent c7b8731 commit fce36f1
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 230 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@
"pako": "2.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-redux": "8.1.2"
"react-redux": "8.1.2",
"rxjs": "8.0.0-alpha.10"
},
"devDependencies": {
"@types/jest": "28.1.8",
Expand Down
67 changes: 0 additions & 67 deletions src/app/actionListener.ts

This file was deleted.

28 changes: 28 additions & 0 deletions src/app/actionObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Middleware, PayloadActionCreator, Action, isAction } from '@reduxjs/toolkit'
import { Observable, Subject, filter, map } from 'rxjs'

interface ActionObserver {
middleware: Middleware
onAction: <TPayload>(actionCreator: PayloadActionCreator<TPayload>) => Observable<TPayload>
}

export const createActionObserver = (): ActionObserver => {
const action$ = new Subject<Action>()

const middleware: Middleware = () => next => action => {
const result = next(action)
if (isAction(action)) {
action$.next(action)
}
return result
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const onAction = (actionCreator: PayloadActionCreator<any>): Observable<any> =>
action$.pipe(
filter(actionCreator.match),
map(action => action.payload)
)

return { middleware, onAction }
}
2 changes: 1 addition & 1 deletion src/app/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ import type { RootState, Store } from './store'

type TypedUseStoreHook = () => Store

export const useStore: TypedUseStoreHook = __useStore
export const useStore = __useStore as TypedUseStoreHook

export const useSelector: TypedUseSelectorHook<RootState> = __useSelector
36 changes: 36 additions & 0 deletions src/app/stateObserver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { Middleware } from '@reduxjs/toolkit'
import { Observable, ReplaySubject, map, distinctUntilChanged, skip } from 'rxjs'
import type { RootState, RootStateSelector } from './store'

interface StateObserver {
middleware: Middleware
onState: <TSelected>(selector: RootStateSelector<TSelected>) => Observable<TSelected>
}

export const createStateObserver = (): StateObserver => {
const state$ = new ReplaySubject<RootState>(1)
const distinctState$ = state$.pipe(distinctUntilChanged())

const middleware: Middleware = api => {
state$.next(api.getState())
let nestedDepth = 0

return next => action => {
try {
nestedDepth += 1
const result = next(action)
if (nestedDepth === 1) {
state$.next(api.getState())
}
return result
} finally {
nestedDepth -= 1
}
}
}

const onState = <TSelected>(selector: RootStateSelector<TSelected>): Observable<TSelected> =>
distinctState$.pipe(map(selector), distinctUntilChanged(), skip(1))

return { middleware, onState }
}
4 changes: 2 additions & 2 deletions src/app/stateSaver.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useEffect } from 'react'
import { useStore } from './hooks'
import { watch } from './watcher'
import { subscribe } from './subscribe'
import { StateToPersist, selectStateToPersist } from './persist'
import { saveState as saveStateToUrl } from './url'
import { saveState as saveStateToLocalStorage } from './localStorage'
Expand All @@ -16,6 +16,6 @@ export const useStateSaver = (): void => {
useEffect(() => {
const stateToPersist = selectStateToPersist(store.getState())
saveState(stateToPersist)
return watch(selectStateToPersist, saveState)
return subscribe(store.onState(selectStateToPersist), saveState)
}, [])
}
29 changes: 19 additions & 10 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import exceptionReducer from '@/features/exception/exceptionSlice'
import { loadState as loadStateFromLocalStorage } from './localStorage'
import { loadState as loadStateFromUrl } from './url'
import { getInitialStateToPersist } from './persist'
import { actionListener } from './actionListener'
import { watcher } from './watcher'
import { createActionObserver } from './actionObserver'
import { createStateObserver } from './stateObserver'
import { merge } from '@/common/utils'

const rootReducer = combineReducers({
Expand All @@ -34,14 +34,23 @@ const getPreloadedState = (): Partial<RootState> => {
return merge(getInitialStateToPersist(), stateFromLocalStorage, stateFromUrl)
}

const store = configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware => {
const defaultMiddleware = getDefaultMiddleware()
return defaultMiddleware.prepend(watcher, actionListener)
},
preloadedState: getPreloadedState()
})
const actionObserver = createActionObserver()
const stateObserver = createStateObserver()

const store = Object.assign(
configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware => {
const defaultMiddleware = getDefaultMiddleware()
return defaultMiddleware.prepend(stateObserver.middleware, actionObserver.middleware)
},
preloadedState: getPreloadedState()
}),
{
onAction: actionObserver.onAction,
onState: stateObserver.onState
}
)

export type Store = typeof store

Expand Down
11 changes: 11 additions & 0 deletions src/app/subscribe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import type { Observable, Observer, Subscription } from 'rxjs'

export type Unsubscribe = Subscription['unsubscribe']

export const subscribe = <T>(
observable: Observable<T>,
observerOrNext?: Partial<Observer<T>> | ((value: T) => void) | null
): Unsubscribe => {
const subscription = observable.subscribe(observerOrNext)
return () => subscription.unsubscribe()
}
84 changes: 0 additions & 84 deletions src/app/watcher.ts

This file was deleted.

3 changes: 3 additions & 0 deletions src/features/assembler/assemblerSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ export const assemblerSlice = createSlice({

export const selectAssembledSource = (state: RootState): string => state.assembler.source

export const selectIsAssembled = (state: RootState): boolean =>
state.assembler.source !== initialState.source

export const selectAddressToStatementMap = (state: RootState): Partial<AddressToStatementMap> =>
state.assembler.addressToStatementMap

Expand Down
Loading

0 comments on commit fce36f1

Please sign in to comment.