Last active
April 19, 2022 08:08
-
-
Save anthonyjoeseph/7463dac5f41d8d2b3d0aaec5c83e3e84 to your computer and use it in GitHub Desktop.
Should I Use redux-observable fp-ts Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { pipe } from 'fp-ts/pipeable' | |
import * as O from 'fp-ts/Option' | |
import * as Op from 'monocle-ts/lib/Optional' | |
import * as r from 'rxjs' | |
import * as ro from 'rxjs/operators' | |
import { createStore, applyMiddleware } from 'redux'; | |
import { createEpicMiddleware } from 'redux-observable'; | |
import React from 'react'; | |
import { Provider, useDispatch, useSelector } from 'react-redux'; | |
import { makeADT, ofType, ADTType } from '@morphic-ts/adt' | |
import { identity } from 'fp-ts/lib/function'; | |
interface State { | |
otherStuff: number | |
asyncData: O.Option<Data> | |
} | |
type Data = { | |
num: number | |
bool: boolean | |
} | |
const fetchAsyncData: () => Promise<Data> = () => new Promise<Data>(r => r({ | |
num: 3, | |
bool: true | |
})) | |
interface FetchData { type: 'FetchData' } | |
interface UpdateData { type: 'UpdateData'; asyncData: O.Option<Data> } | |
const Action = makeADT('type')({ | |
FetchData: ofType<FetchData>(), | |
UpdateData: ofType<UpdateData>() | |
}) | |
type Action = ADTType<typeof Action> | |
const epic = (action$: r.Observable<Action>): r.Observable<Action> => pipe( | |
r.merge( | |
pipe( | |
action$, | |
ro.filter((action: Action): action is FetchData => action.type === 'FetchData'), | |
ro.map((fetchData: FetchData): Promise<Data> => fetchAsyncData()), | |
ro.flatMap((promResponse: Promise<Data>): r.Observable<Data> => r.from(promResponse)), | |
ro.map(O.some), | |
), | |
pipe( | |
r.fromEvent<KeyboardEvent>(window, 'keydown'), | |
ro.filter(e => e.which === 8), // 8 is the key code for 'delete' | |
ro.map(() => O.none), | |
), | |
), | |
ro.map((asyncData: O.Option<Data>): Action => ({ type: 'UpdateData', asyncData })), | |
) | |
const defaultState: State = { | |
otherStuff: 40, | |
asyncData: O.none, | |
} | |
const reducer = Action.createPartialReducer<State>(defaultState)({ | |
UpdateData: (action: UpdateData) => pipe( | |
Op.id<State>(), | |
Op.prop('asyncData'), | |
).set(action.asyncData) | |
}) | |
const epicMiddleware = createEpicMiddleware<Action, Action, State>(); | |
let store = createStore( | |
reducer, | |
applyMiddleware( | |
epicMiddleware, | |
), | |
); | |
epicMiddleware.run(epic); | |
const App = () => ( | |
<Provider | |
store={store} | |
> | |
<ReduxApp /> | |
</Provider> | |
); | |
const ReduxApp = () => { | |
const state = useSelector<State, State>(s => s); | |
const dispatch = useDispatch<(_: Action) => void>() | |
return ( | |
<div> | |
<input | |
type="button" | |
onClick={() => dispatch({ | |
type: 'FetchData' | |
})} | |
/> | |
data: {pipe( | |
state.asyncData, | |
O.map(JSON.stringify), | |
O.getOrElse((): string => 'no data'), | |
)} | |
</div> | |
) | |
} | |
export default App; |
I would use
data: {pipe( state.asyncData, O.map(JSON.stringify), O.getOrElse((): string => 'no data'), )}
inside memoized selector
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Original article