Skip to content

Instantly share code, notes, and snippets.

@anthonyjoeseph
Last active April 19, 2022 08:08
Show Gist options
  • Save anthonyjoeseph/7463dac5f41d8d2b3d0aaec5c83e3e84 to your computer and use it in GitHub Desktop.
Save anthonyjoeseph/7463dac5f41d8d2b3d0aaec5c83e3e84 to your computer and use it in GitHub Desktop.
Should I Use redux-observable fp-ts Example
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;
@anthonyjoeseph
Copy link
Author

@juozasmasiliunas
Copy link

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