immer#produceWithPatches TypeScript Examples

The following examples show how to use immer#produceWithPatches. 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: useSyncState.ts    From core with MIT License 5 votes vote down vote up
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 #2
Source File: model.ts    From reactant with MIT License 5 votes vote down vote up
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 #3
Source File: action.ts    From reactant with MIT License 4 votes vote down vote up
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,
  };
}