UNPKG

mobx-keystone-mindreframer

Version:

A MobX powered state management solution based on data trees with first class support for Typescript, snapshots, patches and much more

121 lines (102 loc) 2.93 kB
import { action, createAtom, IAtom, untracked } from "mobx" import { fastGetParentPath, ParentPath } from "../parent/path" import { debugFreeze } from "../utils" import type { SnapshotOutOf } from "./SnapshotOf" interface SnapshotData<T extends object> { standard: SnapshotOutOf<T> readonly atom: IAtom } const snapshots = new WeakMap<Object, SnapshotData<any>>() /** * @ignore * @internal */ export function getInternalSnapshot<T extends object>( value: T ): Readonly<SnapshotData<T>> | undefined { return snapshots.get(value) as any } function getInternalSnapshotParent( sn: SnapshotData<any>, parentPath: ParentPath<any> | undefined ): { parentSnapshot: SnapshotData<any>; parentPath: ParentPath<any> } | undefined { return untracked(() => { if (!parentPath) { return undefined } const parentSn = getInternalSnapshot(parentPath.parent) if (!parentSn) { return undefined } return sn ? { parentSnapshot: parentSn, parentPath: parentPath, } : undefined }) } /** * @ignore * @internal */ export const unsetInternalSnapshot = action("unsetInternalSnapshot", (value: any) => { const oldSn = getInternalSnapshot(value) as SnapshotData<any> if (oldSn) { snapshots.delete(value) oldSn.atom.reportChanged() } }) /** * @ignore * @internal */ export const setInternalSnapshot = action( "setInternalSnapshot", <T extends object>(value: any, standard: T): void => { const oldSn = getInternalSnapshot(value) as SnapshotData<any> // do not actually update if not needed if (oldSn && oldSn.standard === standard) { return } debugFreeze(standard) let sn: SnapshotData<any> if (oldSn) { sn = oldSn sn.standard = standard } else { sn = { standard, atom: createAtom("snapshot"), } snapshots.set(value, sn) } sn.atom.reportChanged() // also update parent(s) snapshot(s) if needed const parent = getInternalSnapshotParent(oldSn, fastGetParentPath(value)) if (parent) { const { parentSnapshot, parentPath } = parent // might be false in the cases where the parent has not yet been created if (parentSnapshot) { const path = parentPath.path // patches for parent changes should not be emitted let parentStandardSn = parentSnapshot.standard if (parentStandardSn[path] !== sn.standard) { if (Array.isArray(parentStandardSn)) { parentStandardSn = parentStandardSn.slice() } else { parentStandardSn = Object.assign({}, parentStandardSn) } parentStandardSn[path] = sn.standard setInternalSnapshot(parentPath.parent, parentStandardSn) } } } } ) /** * @ignore */ export function reportInternalSnapshotObserved(sn: Readonly<SnapshotData<any>>) { sn.atom.reportObserved() }