Created
August 6, 2019 08:30
-
-
Save freddi301/792e83442bf13e6aaaa0ba8b253c3ee3 to your computer and use it in GitHub Desktop.
Fiber (react hooks outside of react)
This file contains hidden or 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 { any } from "prop-types"; | |
type Cell = { initialized: boolean; state: any; next: any }; | |
export function Cell(): Cell { | |
return { initialized: false, state: null, next: null }; | |
} | |
export type CellFunction = <State, Initial>( | |
callback: (state: State | Initial) => State, | |
init: () => Initial | |
) => State; | |
export type CellFunctionTypeIntance<State, Initial> = ( | |
callback: (state: State | Initial) => State, | |
init: () => Initial | |
) => State; | |
export type Fiber<Action, Return> = ( | |
action: Action, | |
cell: CellFunction | |
) => Return; | |
export function makeFiber<Action, Return>(fiber: Fiber<Action, Return>) { | |
return fiber; | |
} | |
export function fiberRunner<Action, Return>( | |
fiber: Fiber<Action, Return>, | |
rootCell: Cell | |
) { | |
let currentCell = rootCell; | |
function cell(callback: (state: any) => any, init: () => any) { | |
if (!currentCell.initialized) { | |
currentCell.initialized = true; | |
currentCell.state = init(); | |
currentCell.next = Cell(); | |
} | |
const newState = callback(currentCell.state); | |
currentCell.state = newState; | |
currentCell = currentCell.next; | |
return newState; | |
} | |
const next = (action: Action) => { | |
const result = fiber(action, cell); | |
currentCell = rootCell; | |
return result; | |
}; | |
return next; | |
} | |
export function runFiber<Action, Return>( | |
fiber: Fiber<Action, Return>, | |
rootCell: Cell, | |
action: Action | |
) { | |
let currentCell = rootCell; | |
function cell(callback: (state: any) => any, init: () => any) { | |
if (!currentCell.initialized) { | |
currentCell.initialized = true; | |
currentCell.state = init(); | |
currentCell.next = Cell(); | |
} | |
const newState = callback(currentCell.state); | |
currentCell.state = newState; | |
currentCell = currentCell.next; | |
return newState; | |
} | |
const result = fiber(action, cell); | |
return result; | |
} | |
export function cloneCell(original: Cell | null): Cell { | |
if (!original) { | |
return (null as any) as Cell; | |
} | |
const cloned = Cell(); | |
cloned.initialized = original.initialized; | |
cloned.state = original.state; | |
cloned.next = cloneCell(original.next); | |
return cloned; | |
} |
This file contains hidden or 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 { CellFunction, CellFunctionTypeIntance } from "./fiber"; | |
export function memo<T, D>( | |
cell: CellFunction, | |
callback: () => T, | |
deps: D, | |
compare: (prev: D, next: D) => boolean = shallowCompareArray | |
) { | |
const [memoized] = cell<[T, D], null>( | |
state => { | |
if (state !== null) { | |
const [memoized, lastDeps] = state; | |
if (compare(lastDeps, deps)) { | |
return [memoized, deps]; | |
} | |
} | |
return [callback(), deps]; | |
}, | |
() => null | |
); | |
return memoized; | |
} | |
export function effect<D>( | |
cell: CellFunction, | |
callback: () => () => void, | |
deps: D, | |
compare: (prev: D, next: D) => boolean = shallowCompareArray | |
) { | |
cell<[() => void, D], null>( | |
state => { | |
if (state === null) { | |
return [callback(), deps]; | |
} else { | |
const [teardown, lastDeps] = state; | |
if (compare(lastDeps, deps)) { | |
return [teardown, deps]; | |
} else { | |
teardown(); | |
return [callback(), deps]; | |
} | |
} | |
}, | |
() => null | |
); | |
} | |
export function ref<T>(cell: CellFunctionTypeIntance<T, T>, callback: () => T) { | |
return cell(x => x, callback); | |
} | |
function shallowCompareArray(a: unknown, b: unknown): boolean { | |
if (!(a instanceof Array) || !(b instanceof Array)) return false; | |
if (a.length !== b.length) return false; | |
for (let i = 0; i < a.length; i++) { | |
if (a[i] !== b[i]) return false; | |
} | |
return true; | |
} |
This file contains hidden or 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 { Fiber, fiberRunner, Cell } from "./fiber"; | |
export function fiberLoop<Action, Return>( | |
fiberWithDispatch: ( | |
action: Action, | |
cell: <State>( | |
callback: (state: State) => State, | |
init: () => State | |
) => State, | |
dispatch: (action: Action) => void | |
) => Return, | |
init: Action | |
) { | |
const queue: Action[] = []; | |
let scheduled: Promise<void> | null = null; | |
function dispatch(action: Action) { | |
queue.push(action); | |
if (!scheduled) { | |
scheduled = Promise.resolve().then(run); | |
} | |
} | |
function run() { | |
while (queue.length > 0) { | |
const action = queue.shift() as Action; | |
const result = runner(action); | |
publish([action, result]); | |
} | |
scheduled = null; | |
} | |
const fiber: Fiber<Action, Return> = (action, cell) => | |
fiberWithDispatch(action, cell, dispatch); | |
const rootCell = Cell(); | |
const runner = fiberRunner(fiber, rootCell); | |
const [state, subscribe, publish] = subjectStateful<[Action, Return]>([ | |
init, | |
runner(init) | |
]); | |
return { dispatch, subscribe, state }; | |
} | |
function subjectStateful<T>(initial: T) { | |
type Listener = (event: T) => void; | |
type Subscription = { listener: Listener }; | |
const subscriptions = new Set<Subscription>(); | |
let currentState = initial; | |
function subscribe(listener: Listener) { | |
const subscription = { listener }; | |
subscriptions.add(subscription); | |
return () => { | |
subscriptions.delete(subscription); | |
}; | |
} | |
function publish(event: T) { | |
currentState = event; | |
for (const { listener } of subscriptions) { | |
listener(event); | |
} | |
} | |
function state() { | |
return currentState; | |
} | |
return [state, subscribe, publish] as const; | |
} | |
// function subject<T>() { | |
// type Listener = (event: T) => void; | |
// type Subscription = { listener: Listener }; | |
// const subscriptions = new Set<Subscription>(); | |
// function subscribe(listener: Listener) { | |
// const subscription = { listener }; | |
// subscriptions.add(subscription); | |
// return () => { | |
// subscriptions.delete(subscription); | |
// }; | |
// } | |
// function publish(event: T) { | |
// for (const { listener } of subscriptions) { | |
// listener(event); | |
// } | |
// } | |
// return [subscribe, publish] as const; | |
// } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment