Skip to content

Commit

Permalink
refactor: remove react-redux
Browse files Browse the repository at this point in the history
  • Loading branch information
exuanbo committed Aug 13, 2023
1 parent 8be65f2 commit 8087555
Show file tree
Hide file tree
Showing 19 changed files with 181 additions and 284 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@
"pako": "2.1.0",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-redux": "8.1.2",
"rxjs": "8.0.0-alpha.10"
"rxjs": "8.0.0-alpha.10",
"use-sync-external-store": "1.2.0"
},
"devDependencies": {
"@types/jest": "28.1.8",
"@types/pako": "2.0.0",
"@types/react": "18.2.18",
"@types/react-dom": "18.2.7",
"@types/use-sync-external-store": "0.0.3",
"@vitejs/plugin-react": "4.0.4",
"jest": "28.1.3",
"jest-environment-jsdom": "28.1.3",
Expand Down
28 changes: 19 additions & 9 deletions src/app/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import {
TypedUseSelectorHook,
useStore as __useStore,
useSelector as __useSelector
} from 'react-redux'
import type { RootState, Store } from './store'
import { useDebugValue } from 'react'
import { useSyncExternalStoreWithSelector } from 'use-sync-external-store/with-selector'
import { RootStateSelector, store } from './store'

type TypedUseStoreHook = () => Store
type EqualityFn<T> = (a: T, b: T) => boolean

export const useStore = __useStore as TypedUseStoreHook
const refEquality: EqualityFn<unknown> = (a, b) => a === b

export const useSelector: TypedUseSelectorHook<RootState> = __useSelector
export const useSelector = <TSelected>(
selector: RootStateSelector<TSelected>,
equalityFn: EqualityFn<TSelected> = refEquality
): TSelected => {
const selectedState = useSyncExternalStoreWithSelector(
store.subscribe,
store.getState,
null,
selector,
equalityFn
)
useDebugValue(selectedState)
return selectedState
}
6 changes: 2 additions & 4 deletions src/app/stateSaver.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect } from 'react'
import { useStore } from './hooks'
import { store, applySelector } from './store'
import { subscribe } from './subscribe'
import { StateToPersist, selectStateToPersist } from './persist'
import { saveState as saveStateToUrl } from './url'
Expand All @@ -11,10 +11,8 @@ const saveState = (state: StateToPersist): void => {
}

export const useStateSaver = (): void => {
const store = useStore()

useEffect(() => {
const stateToPersist = selectStateToPersist(store.getState())
const stateToPersist = applySelector(selectStateToPersist)
saveState(stateToPersist)
return subscribe(store.onState(selectStateToPersist), saveState)
}, [])
Expand Down
5 changes: 3 additions & 2 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const getPreloadedState = (): Partial<RootState> => {
const actionObserver = createActionObserver()
const stateObserver = createStateObserver()

const store = Object.assign(
export const store = Object.assign(
configureStore({
reducer: rootReducer,
middleware: getDefaultMiddleware => {
Expand All @@ -54,4 +54,5 @@ const store = Object.assign(

export type Store = typeof store

export default store
export const applySelector = <TSelected>(selector: RootStateSelector<TSelected>): TSelected =>
selector(store.getState())
51 changes: 23 additions & 28 deletions src/features/assembler/assemble.ts
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
import type { Store } from '@/app/store'
import { store } from '@/app/store'
import { AssembleResult, AssemblerError, assemble as assemblePure } from './core'
import { setAssemblerState, setAssemblerError } from './assemblerSlice'
import { setMemoryDataFrom } from '@/features/memory/memorySlice'
import { resetCpuState } from '@/features/cpu/cpuSlice'
import { setEditorHighlightRange, clearEditorHighlightRange } from '@/features/editor/editorSlice'
import { setException } from '@/features/exception/exceptionSlice'

type Assemble = (input: string) => void

export const createAssemble = (store: Store): Assemble => {
const assemble: Assemble = input => {
let assembleResult: AssembleResult
try {
assembleResult = assemblePure(input)
} catch (exception) {
if (exception instanceof AssemblerError) {
const assemblerErrorObject = exception.toPlainObject()
store.dispatch(setAssemblerError(assemblerErrorObject))
store.dispatch(clearEditorHighlightRange())
} else {
store.dispatch(setException(exception))
}
return
}
const [addressToOpcodeMap, addressToStatementMap] = assembleResult
store.dispatch(setAssemblerState({ source: input, addressToStatementMap }))
store.dispatch(setMemoryDataFrom(addressToOpcodeMap))
store.dispatch(resetCpuState())
const firstStatement = addressToStatementMap[0]
const hasStatement = firstStatement !== undefined
if (hasStatement) {
store.dispatch(setEditorHighlightRange(firstStatement))
} else {
export const assemble = (input: string): void => {
let assembleResult: AssembleResult
try {
assembleResult = assemblePure(input)
} catch (exception) {
if (exception instanceof AssemblerError) {
const assemblerErrorObject = exception.toPlainObject()
store.dispatch(setAssemblerError(assemblerErrorObject))
store.dispatch(clearEditorHighlightRange())
} else {
store.dispatch(setException(exception))
}
return
}
const [addressToOpcodeMap, addressToStatementMap] = assembleResult
store.dispatch(setAssemblerState({ source: input, addressToStatementMap }))
store.dispatch(setMemoryDataFrom(addressToOpcodeMap))
store.dispatch(resetCpuState())
const firstStatement = addressToStatementMap[0]
const hasStatement = firstStatement !== undefined
if (hasStatement) {
store.dispatch(setEditorHighlightRange(firstStatement))
} else {
store.dispatch(clearEditorHighlightRange())
}
return assemble
}
6 changes: 2 additions & 4 deletions src/features/controller/ConfigurationMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import MenuButton from './MenuButton'
import MenuItems from './MenuItems'
import MenuItem from './MenuItem'
import { CheckMark, Wrench } from '@/common/components/icons'
import { useStore, useSelector } from '@/app/hooks'
import { store } from '@/app/store'
import { useSelector } from '@/app/hooks'
import {
ClockSpeed,
clockSpeedOptionNames,
Expand All @@ -18,7 +19,6 @@ import {
} from './controllerSlice'

const AutoAssembleSwitch = (): JSX.Element => {
const store = useStore()
const autoAssemble = useSelector(selectAutoAssemble)

const toggleAutoAssemble = (): void => {
Expand All @@ -36,7 +36,6 @@ const AutoAssembleSwitch = (): JSX.Element => {
}

const ClockSpeedMenu = (): JSX.Element => {
const store = useStore()
const clockSpeed = useSelector(selectClockSpeed)

return (
Expand Down Expand Up @@ -74,7 +73,6 @@ const ClockSpeedMenu = (): JSX.Element => {
}

const TimerIntervalMenu = (): JSX.Element => {
const store = useStore()
const timerInterval = useSelector(selectTimerInterval)

return (
Expand Down
114 changes: 51 additions & 63 deletions src/features/controller/FileMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,30 +5,26 @@ import MenuItems from './MenuItems'
import MenuItem from './MenuItem'
import Modal from '@/common/components/Modal'
import { File as FileIcon } from '@/common/components/icons'
import { useStore } from '@/app/hooks'
import { store, applySelector } from '@/app/store'
import { setEditorInput, selectEditorInput } from '@/features/editor/editorSlice'
import { template, examples } from '@/features/editor/examples'

const NewFileButton = (): JSX.Element => {
const store = useStore()

return (
<MenuItem
onClick={() => {
store.dispatch(
setEditorInput({
value: template.content,
isFromFile: true
})
)
}}>
<MenuButton>
<span className="w-4" />
<span>New File</span>
</MenuButton>
</MenuItem>
)
}
const NewFileButton = (): JSX.Element => (
<MenuItem
onClick={() => {
store.dispatch(
setEditorInput({
value: template.content,
isFromFile: true
})
)
}}>
<MenuButton>
<span className="w-4" />
<span>New File</span>
</MenuButton>
</MenuItem>
)

interface OpenButtonProps {
onFileLoad: () => void
Expand All @@ -46,8 +42,6 @@ const OpenButton = ({ onFileLoad }: OpenButtonProps): JSX.Element => {
event.stopPropagation()
}

const store = useStore()

const loadFile = (file: File): void => {
const reader = Object.assign(new FileReader(), {
onload: () => {
Expand Down Expand Up @@ -81,49 +75,43 @@ const OpenButton = ({ onFileLoad }: OpenButtonProps): JSX.Element => {
)
}

const OpenExampleMenu = (): JSX.Element => {
const store = useStore()

return (
<MenuItem.Expandable>
{(isHovered, menuItemsRef, menuItemElement) => (
<>
<MenuButton>
<span className="w-4" />
<span>Open Example</span>
</MenuButton>
{isHovered && (
<MenuItems.Expanded innerRef={menuItemsRef} menuItemElement={menuItemElement}>
{examples.map(({ title, content }, index) => (
<MenuItem
key={index}
onClick={() => {
store.dispatch(
setEditorInput({
value: content,
isFromFile: true
})
)
}}>
<MenuButton>
<span className="w-4" />
<span>{title}</span>
</MenuButton>
</MenuItem>
))}
</MenuItems.Expanded>
)}
</>
)}
</MenuItem.Expandable>
)
}
const OpenExampleMenu = (): JSX.Element => (
<MenuItem.Expandable>
{(isHovered, menuItemsRef, menuItemElement) => (
<>
<MenuButton>
<span className="w-4" />
<span>Open Example</span>
</MenuButton>
{isHovered && (
<MenuItems.Expanded innerRef={menuItemsRef} menuItemElement={menuItemElement}>
{examples.map(({ title, content }, index) => (
<MenuItem
key={index}
onClick={() => {
store.dispatch(
setEditorInput({
value: content,
isFromFile: true
})
)
}}>
<MenuButton>
<span className="w-4" />
<span>{title}</span>
</MenuButton>
</MenuItem>
))}
</MenuItems.Expanded>
)}
</>
)}
</MenuItem.Expandable>
)

const SaveButton = (): JSX.Element => {
const store = useStore()

const handleClick = (): void => {
const editorInput = selectEditorInput(store.getState())
const editorInput = applySelector(selectEditorInput)
const fileBlob = new Blob([editorInput], { type: 'application/octet-stream' })
const fileUrl = URL.createObjectURL(fileBlob)
const anchorElement = Object.assign(document.createElement('a'), {
Expand Down
5 changes: 2 additions & 3 deletions src/features/controller/ViewMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ import MenuButton from './MenuButton'
import MenuItems from './MenuItems'
import MenuItem from './MenuItem'
import { CheckMark, View as ViewIcon } from '@/common/components/icons'
import { useStore, useSelector } from '@/app/hooks'
import { store } from '@/app/store'
import { useSelector } from '@/app/hooks'
import { memoryViewOptions, selectMemoryView, setMemoryView } from '@/features/memory/memorySlice'
import { ioDeviceNames, selectIoDeviceStates, toggleIoDeviceVisible } from '@/features/io/ioSlice'
import { splitCamelCaseToString } from '@/common/utils'

const MemoryMenu = (): JSX.Element => {
const store = useStore()
const memoryView = useSelector(selectMemoryView)

return (
Expand Down Expand Up @@ -43,7 +43,6 @@ const MemoryMenu = (): JSX.Element => {
}

const IoMenu = (): JSX.Element => {
const store = useStore()
const ioDeviceStates = useSelector(selectIoDeviceStates)

return (
Expand Down
Loading

0 comments on commit 8087555

Please sign in to comment.