Last active
May 18, 2018 19:30
-
-
Save yuretz/cdba9cb6a0d74479da0570f5ee357cc0 to your computer and use it in GitHub Desktop.
hyperHTML + Redux + TypeScript = Awesomeness
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 hyper from 'hyperhtml/esm'; | |
import { applyMiddleware, combineReducers, createStore, Store, Unsubscribe, Action, Dispatch } from 'redux'; | |
import { Selector } from 'reselect'; | |
export interface AppState { | |
// ... | |
// your app state interface here | |
} | |
export const appStateStore = createStore<AppState>( | |
combineReducers({ | |
// ... some AppState reducers here | |
}), | |
applyMiddleware(/* ... some middleware here */) | |
); | |
/** | |
* Initializes the view instance | |
* | |
* @param view View instance to initialize | |
* @param selectors List of selectors to subscribe the view to | |
*/ | |
export function initView<V extends View<any>, T = InferModelType<V>>( | |
view: V, | |
...selectors: Selector<AppState, Partial<T>>[] | |
): V { | |
view.setDispatch(appStateStore.dispatch); | |
selectors.forEach((item) => subscribeView(appStateStore, view, item)); | |
return view; | |
} | |
/** | |
* Base class for all view classes | |
* | |
* @template T The view's model type | |
*/ | |
export class View<T> extends hyper.Component<T> { | |
constructor(initialState?: T | undefined) { | |
super(); | |
if(initialState) { | |
this.setState(initialState); | |
} | |
} | |
/** | |
* Returns the value indicating whether the view is subscribed | |
*/ | |
get subscribed(): boolean { | |
return !!this._unsubscribes.length; | |
} | |
/** | |
* Passes the dispatch function to the view | |
* | |
* @param dispatch The dispatch function | |
* @memberof View | |
*/ | |
setDispatch(dispatch: Dispatch<Action>) { | |
this.dispatch = dispatch; | |
} | |
/** | |
* Registers the view subscription | |
* | |
* @param {() => Unsubscribe} subscription The subscribe function | |
* @memberof View | |
*/ | |
register(subscription: () => Unsubscribe) { | |
this._subscribes.push(subscription); | |
} | |
/** | |
* View disconnection event handler | |
* | |
* @protected | |
* @memberof View | |
*/ | |
protected ondisconnected() { | |
// call all the unsubscribe() functions | |
this._unsubscribes.forEach(Function.call.bind(Function.call)); | |
this._unsubscribes = []; | |
} | |
/** | |
* View connection event handler | |
* | |
* @protected | |
* @memberof View | |
*/ | |
protected onconnected() { | |
// call all the subscribe() and save all unsubscribe() functions | |
this._unsubscribes = this._subscribes.map(Function.call.bind(Function.call)); | |
} | |
/** | |
* Event dispatching function | |
* | |
* @memberof View | |
*/ | |
protected dispatch: Dispatch<Action> = (_: any) => {throw new Error('dispatch missing'); }; | |
private _subscribes: (() => Unsubscribe)[] = []; | |
private _unsubscribes: Unsubscribe[] = []; | |
} | |
// type inference helper | |
type InferModelType<V> = V extends View<infer M> ? M : never; | |
/** | |
* Subscribes the view to updates from the Redux store | |
* | |
* @export | |
* @template TAppState Application state type | |
* @template TModel View state type | |
* @param {Store<TAppState>} store The Redux store | |
* @param {EpokeView<TModel>} view The view instance | |
* @param {Selector<TAppState, Partial<TModel>>} selector The view state selector function | |
*/ | |
function subscribeView<TAppState, TModel>( | |
store: Store<TAppState>, | |
view: View<TModel>, | |
selector: Selector<TAppState, Partial<TModel>> | |
) { | |
view.register(function subscribe() { | |
let current = selector(store.getState()); | |
view.setState(current); | |
return store.subscribe(function listener() { | |
const state = selector(store.getState()); | |
if (state !== current) { | |
current = state; | |
view.setState(state); | |
} | |
}); | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment