Skip to content

Instantly share code, notes, and snippets.

@freddi301
Created August 6, 2019 08:30
Show Gist options
  • Save freddi301/792e83442bf13e6aaaa0ba8b253c3ee3 to your computer and use it in GitHub Desktop.
Save freddi301/792e83442bf13e6aaaa0ba8b253c3ee3 to your computer and use it in GitHub Desktop.
Fiber (react hooks outside of react)
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;
}
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;
}
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