Created
August 20, 2021 19:35
-
-
Save AlexandrHoroshih/4ca98a746ec2bed163cfe11552e2d938 to your computer and use it in GitHub Desktop.
Effector history wrapper
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
// an option to create bindings to history | |
// usage is like | |
// | |
// export const historyUpdated = createEvent<HistoryUpdate>(); | |
// export const clocks = createHistoryClocks(); | |
// | |
// clocks have fields like `push, replace, go` and so on | |
// can be used like clocks.push({ to: "path" }) | |
// | |
// wrapHistory({ | |
// historySource: $history, // put history instance to store | |
// clocks, | |
// target: historyUpdated.prepend<HistoryUpdate>((update) => klona(update)), | |
// // history is mutable, it is much safer having an immutable copy of every update | |
// }); | |
// | |
import type { Action, History, Location, LocationState, Path } from "history"; | |
import type { Domain, Event, Store } from "effector"; | |
import { createEvent, sample, scopeBind } from "effector"; | |
export type ToParams<S extends LocationState = LocationState> = { | |
to: Path; | |
LocationState?: S; | |
}; | |
export type HistoryUpdate = { action: Action; location: Location }; | |
export type Clocks<S extends LocationState> = { | |
push: Event<ToParams<S>>; | |
replace: Event<ToParams<S>>; | |
go: Event<number>; | |
back: Event<unknown>; | |
forward: Event<unknown>; | |
}; | |
type Config<S extends LocationState = LocationState> = { | |
historySource: Store<History<S> | null>; | |
clocks: Clocks<S>; | |
target: Event<HistoryUpdate>; | |
}; | |
const checkHistory = <S extends LocationState>( | |
history?: History<S> | null, | |
): history is History<S> => { | |
const historyProvided = Boolean(history); | |
if (!historyProvided) { | |
console.warn("No history was provided"); | |
return false; | |
} | |
return historyProvided; | |
}; | |
export const createHistoryClocks = (domain?: Domain) => { | |
if (domain) { | |
return { | |
push: domain.createEvent<ToParams>(), | |
replace: domain.createEvent<ToParams>(), | |
go: domain.createEvent<number>(), | |
back: domain.createEvent<unknown>(), | |
forward: domain.createEvent<unknown>(), | |
}; | |
} | |
return { | |
push: createEvent<ToParams>(), | |
replace: createEvent<ToParams>(), | |
go: createEvent<number>(), | |
back: createEvent<unknown>(), | |
forward: createEvent<unknown>(), | |
}; | |
}; | |
export const wrapHistory = <S extends LocationState = LocationState>( | |
config: Config<S>, | |
) => { | |
const { historySource, clocks, target } = config; | |
historySource.updates.watch((history) => { | |
// Hacky way to support both in and out of scope listeners | |
// eslint-disable-next-line @typescript-eslint/no-unused-vars | |
let listener = (_: any) => console.warn("History listener is not set"); | |
try { | |
listener = scopeBind(target); | |
} catch (e) { | |
listener = target; | |
} | |
if (!checkHistory<S>(history)) return; | |
listener({ location: history.location, action: history.action }); | |
history.listen(() => { | |
listener({ location: history.location, action: history.action }); | |
}); | |
}); | |
// push | |
const historyPushed = sample({ | |
source: historySource, | |
clock: clocks.push, | |
fn: (history, params) => ({ history, params }), | |
}); | |
historyPushed.watch( | |
({ history, params }) => | |
checkHistory(history) && history.push(params.to, params.LocationState), | |
); | |
// replace | |
const historyReplaced = sample({ | |
source: historySource, | |
clock: clocks.replace, | |
fn: (history, params) => ({ history, params }), | |
}); | |
historyReplaced.watch( | |
({ history, params }) => | |
checkHistory(history) && history.replace(params.to, params.LocationState), | |
); | |
// go | |
const historyGo = sample({ | |
source: historySource, | |
clock: clocks.go, | |
fn: (history, params) => ({ history, params }), | |
}); | |
historyGo.watch( | |
({ history, params }) => checkHistory(history) && history.go(params), | |
); | |
// back | |
const historyBack = sample({ | |
source: historySource, | |
clock: clocks.back, | |
fn: (history) => history, | |
}); | |
historyBack.watch((history) => checkHistory(history) && history.goBack()); | |
// forward | |
const historyForward = sample({ | |
source: historySource, | |
clock: clocks.forward, | |
fn: (history) => history, | |
}); | |
historyForward.watch( | |
(history) => checkHistory(history) && history.goForward(), | |
); | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment