Skip to content

Instantly share code, notes, and snippets.

Last active June 6, 2021 16:25
What would you like to do?
import { useEffect, useMemo, useRef } from "preact/hooks"
type PropsChangeMode = 'notify' | 'recycle' | 'smart'
interface IControllerClass<T, P> {
new(props: P): T
dispose?: () => void
propsDidChange?: (props: P) => void
* Creates a controller, which is a class that has the optional methods: `dispose()` and `propsDidChange(newProps: T)`.
* @param klass Controller class to instantiate
* @param props Props to send to the controller
* @param deps Dependencies to track - Behavior when changes are detected are dictated by `recycle`
* @param changeMode if `notify`, changes to deps will call the `propsDidChange()` method on the current instance. If `recycle`, it disposes of the current instance and creates a new one. If `smart` (the default), it will look at the controller and if a `propsDidChange` method is found, mode will be notify, otherwise it will recycle.
export function useController<T, P>(klass: IControllerClass<T, P>, props: P, deps: any[] = [], changeMode: PropsChangeMode = 'smart'): T {
const instance = useRef<T | null>(null)
useMemo(() => {
if (shouldRecycle(changeMode, klass)) {
instance.current = new klass(props)
else {
if (!instance.current) {
instance.current = new klass(props)
else {
notifyController(instance, props)
return instance.current
}, deps)
useEffect(() => () => disposeController(instance), []) // Dispose on dismount
return instance.current as T
const disposeController = (ref: any) => ref?.current?.dispose?.()
const notifyController = (ref: any, props: any) => ref?.current?.propsDidChange?.(props)
const hasNotificationMethod = <T, P>(klass: IControllerClass<T, P>) => (
'propsDidChange' in klass.prototype ||
'propsDidChange' in klass
function shouldRecycle<T, P>(mode: PropsChangeMode, klass: IControllerClass<T, P>): boolean {
if (mode == 'recycle') return true
if (mode == 'notify') return false
return hasNotificationMethod(klass) ? false : true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment