immer#Patch TypeScript Examples
The following examples show how to use
immer#Patch.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: slot.ts From textbus with GNU General Public License v3.0 | 6 votes |
/**
* 更新插槽状态的方法
* @param fn
*/
updateState(fn: (draft: Draft<T>) => void): T {
let changes!: Patch[]
let inverseChanges!: Patch[]
const oldState = this.state
const newState = produce(oldState, fn, (p, ip) => {
changes = p
inverseChanges = ip
})
this.state = newState
const applyAction: ApplyAction = {
type: 'apply',
patches: changes,
value: newState
}
this.changeMarker.markAsDirtied({
path: [],
apply: [applyAction],
unApply: [{
type: 'apply',
patches: inverseChanges,
value: oldState
}]
})
this.stateChangeEvent.next([applyAction])
return newState!
}
Example #2
Source File: useStateTracking.ts From baleen3 with Apache License 2.0 | 5 votes |
export function useStateTracking<T>(initialState: T): {
current: T
modify: (mutator: Mutator<T>) => void
initialize: (initializer: Initializer<T>) => void
undo: () => void
redo: () => void
canUndo: boolean
canRedo: boolean
} {
const [current, setCurrent] = useState<T>(initialState)
const undoStack = useRef<
Array<{ patches: Patch[]; inversePatches: Patch[] }>
>([])
const undoStackPointer = useRef(-1)
const undo = useCallback((): void => {
if (undoStackPointer.current < 0) {
return
}
setCurrent(
(state) =>
applyPatches(
state,
undoStack.current[undoStackPointer.current].inversePatches
) as T
)
undoStackPointer.current--
}, [])
const redo = useCallback((): void => {
if (undoStackPointer.current === undoStack.current.length - 1) {
return
}
undoStackPointer.current++
setCurrent(
(state) =>
applyPatches(
state,
undoStack.current[undoStackPointer.current].patches
) as T
)
}, [])
const modify = useCallback((mutator: Mutator<T>): void => {
setCurrent((state) =>
produce(state, mutator, (patches, inversePatches) => {
// ignore empty change
if (patches.length > 0) {
const pointer = ++undoStackPointer.current
undoStack.current.length = pointer
undoStack.current[Number(pointer)] = { patches, inversePatches }
}
})
)
}, [])
const initialize = useCallback((initializer: Initializer<T>): void => {
setCurrent(initializer)
undoStackPointer.current = -1
undoStack.current.length = 0
}, [])
return {
current,
initialize,
modify,
undo,
redo,
canUndo: undoStackPointer.current > -1,
canRedo: undoStackPointer.current < undoStack.current.length - 1,
}
}
Example #3
Source File: useSyncState.ts From core with MIT License | 5 votes |
export default function useSyncState(
store: DocStore,
subtree: string,
path: string
) {
let stateAtPath = store.getStateAtPath(subtree, path);
return [
stateAtPath,
(callbackOrData: any) => {
let newPath = path; // Avoid mutating the closure value of path
// Do NOT use above stateAtPath, if you do, you get stale value in the closure if you are reusing this setter callback
let stateAtPath = store.getStateAtPath(subtree, newPath);
let replaceValue = false;
if (typeof callbackOrData !== 'function') {
// throw new Error(
// 'Received an object. Expecting a callback function which receives the object you want to modify.'
// );
replaceValue = true;
}
let produceCallback = (draft: any) => {
callbackOrData(draft);
};
if (replaceValue) {
// replace the received value in its parent
// let parentPath = [...path];
const immerPath = jsonPatchPathToImmerPath(newPath);
const childKey = immerPath.pop();
newPath = immerPathToJsonPatchPath(immerPath); // immerPath.join('/');
stateAtPath = store.getStateAtPath(subtree, newPath);
produceCallback = (draft: any) => {
if (childKey) {
draft[childKey] = callbackOrData;
} else {
// if root path
throw new Error('Cannot replace root doc');
}
};
}
// @ts-ignore
let [nextState, patches, inversePatches] = produceWithPatches(
stateAtPath,
produceCallback
);
// console.log(path, 'path');
// console.log(JSON.stringify(patches, null, 2), 'patches before');
patches = patches.map((p: Patch) => {
return {
...p,
path: newPath + immerPathToJsonPatchPath(p.path),
};
});
inversePatches = inversePatches.map((p: any) => {
return { ...p, path: newPath + immerPathToJsonPatchPath(p.path) };
});
// console.log(JSON.stringify(patches, null, 2), 'patches');
patches.forEach((patch: any, index: number) => {
store.dispatch({
type: 'PATCH',
payload: { patch, inversePatch: inversePatches[index], subtree },
});
});
// store.dispatch({
// type: 'PATCHES',
// payload: patches.map((patch: any, index: number) => ({
// patch: patch,
// inversePatch: inversePatches[index],
// })),
// });
},
store.dispatch,
];
}
Example #4
Source File: model.ts From reactant with MIT License | 5 votes |
model = <
S extends Record<string, any>,
A extends Record<string, (...args: any[]) => (state: S) => void>
>(
scheme: Scheme<S, A>
): Actions<A> & Service<S> & S => {
let module: Service<S>;
Object.keys(scheme.actions).forEach((key) => {
const fn = scheme.actions[key];
Object.assign(scheme.actions, {
[key]: (...args: any[]) => {
let state: Immutable<S> | S | undefined;
let patches: Patch[] | undefined;
let inversePatches: Patch[] | undefined;
if (module[enablePatchesKey]) {
[state, patches, inversePatches] = produceWithPatches(
module[stateKey],
(draftState: S) => {
fn(...args)(draftState);
}
);
} else {
state = produce(module[stateKey], (draftState: S) => {
fn(...args)(draftState);
});
}
const lastState = module[storeKey]?.getState();
module[storeKey]!.dispatch({
type: module[identifierKey],
method: key,
state: {
...lastState,
[module[identifierKey]!]: state,
},
_reactant: actionIdentifier,
...(module[enablePatchesKey]
? {
_patches: patches,
_inversePatches: inversePatches,
}
: {}),
});
},
});
});
module = {
[nameKey]: scheme.name,
[stateKey]: {
...scheme.state,
},
...scheme.actions,
};
return module as any;
}
Example #5
Source File: component.ts From textbus with GNU General Public License v3.0 | 4 votes |
/**
* Textbus 扩展组件方法
* @param options
*/
export function defineComponent<Extends extends ComponentExtends,
State = any>(options: ComponentOptions<Extends, State>): Component<ComponentInstance<Extends, State>, ComponentData<State>> {
return {
name: options.name,
markdownSupport: options.markdownSupport,
createInstance(contextInjector: Injector, initData?: ComponentData<State>) {
const marker = new ChangeMarker()
const stateChangeSubject = new Subject<any>()
const onStateChange = stateChangeSubject.asObservable()
const changeController: ChangeController<State> = {
update(fn) {
return componentInstance.updateState(fn)
},
onChange: onStateChange
}
const componentInstance: ComponentInstance<Extends, State> = {
changeMarker: marker,
parent: null,
get parentComponent() {
return componentInstance.parent?.parent || null
},
get state() {
return state!
},
name: options.name,
length: 1,
onStateChange,
type: options.type,
slots: null as any,
extends: null as any,
shortcutList: null as any,
updateState(fn) {
let changes!: Patch[]
let inverseChanges!: Patch[]
const oldState = state
const newState = produce(oldState, fn, (p, ip) => {
changes = p
inverseChanges = ip
}) as State
state = newState
stateChangeSubject.next(newState)
marker.markAsDirtied({
path: [],
apply: [{
type: 'apply',
patches: changes!,
value: newState
}],
unApply: [{
type: 'apply',
patches: inverseChanges!,
value: oldState
}]
})
return newState
},
toJSON() {
return {
name: options.name,
state: state!,
slots: componentInstance.slots.toJSON()
}
},
toString() {
return componentInstance.slots.toString()
}
}
const context: ComponentContext<State> = {
contextInjector,
changeController,
slots: new Slots(componentInstance),
componentInstance: componentInstance,
dynamicShortcut: [],
eventCache: new EventCache<EventTypes>(),
}
contextStack.push(context)
componentInstance.extends = options.setup(initData)
onDestroy(() => {
eventCacheMap.delete(componentInstance)
subscriptions.forEach(i => i.unsubscribe())
})
eventCacheMap.set(componentInstance, context.eventCache)
contextStack.pop()
componentInstance.slots = context.slots
componentInstance.shortcutList = context.dynamicShortcut
let state = Reflect.has(context, 'initState') ? context.initState : initData?.state
const subscriptions: Subscription[] = [
context.slots.onChange.subscribe(ops => {
marker.markAsDirtied(ops)
}),
context.slots.onChildComponentRemoved.subscribe(instance => {
marker.recordComponentRemoved(instance)
}),
context.slots.onChildSlotChange.subscribe(d => {
marker.markAsDirtied(d.operation)
}),
context.slots.onChildSlotForceChange.subscribe(() => {
marker.forceMarkDirtied()
})
]
return componentInstance
}
}
}
Example #6
Source File: action.ts From reactant with MIT License | 4 votes |
action = (
target: object,
key: string,
descriptor: TypedPropertyDescriptor<(...args: any[]) => void>
) => {
const fn = descriptor.value;
if (typeof fn !== 'function') {
throw new Error(
`${String(key)} can only be decorated by '@action' as a class method.`
);
}
const value = function (this: Service, ...args: unknown[]) {
if (typeof this[storeKey] === 'undefined') {
throw new Error(
`'this' in method '${key.toString()}' of class '${
target.constructor.name
}' decorated by '@action' must be bound to the current class instance.`
);
}
let time: number;
if (__DEV__) {
time = Date.now();
}
if (typeof stagedState === 'undefined') {
try {
const lastState = this[storeKey]?.getState();
let state: Record<string, unknown>;
let patches: Patch[] | undefined;
let inversePatches: Patch[] | undefined;
if (this[enablePatchesKey]) {
[state, patches, inversePatches] = produceWithPatches<
Record<string, unknown>
>(lastState, (draftState) => {
stagedState = draftState;
fn.apply(this, args);
});
} else {
state = produce<Record<string, unknown>>(lastState, (draftState) => {
stagedState = draftState;
fn.apply(this, args);
});
}
stagedState = undefined;
if (__DEV__) {
if (lastState === state) {
console.warn(`There are no state updates to method '${fn.name}'`);
}
// performance checking
const executionTime = Date.now() - time!;
if (executionTime > 100)
console.warn(
`The execution time of method '${this[
identifierKey
]?.toString()}.${key.toString()}' is ${executionTime} ms, it's recommended to use 'dispatch()' API.`
);
// performance detail: https://immerjs.github.io/immer/docs/performance
}
this[storeKey]!.dispatch<ReactantAction>({
type: this[identifierKey]!,
method: key,
params: args,
state,
_reactant: actionIdentifier,
...(this[enablePatchesKey]
? {
_patches: patches,
_inversePatches: inversePatches,
}
: {}),
});
} finally {
stagedState = undefined;
}
} else {
// enable staged state mode.
fn.apply(this, args);
}
};
return {
...descriptor,
value,
};
}