Skip to content

Patches

Structura can create a serializable list of patches with all the modifications that were applied in a producer, and can also obtain the inverse modifications to return to the original state.

Patches and inverse patches are useful for example if you want to send them over a network or to implement undo/redo functionality.

Patches do not comply with RFC 6902, so if you want to use them in another language/library you should create your own parser but you can change this by turning a setting on. Alternatively there is a converter which can turn patches generated by structura into standard JSON Patches.

Example with callback

typescript
type Make = () => Record<string, number>[]
const makeObj: Make = () => [{ A: 1 }];

// first we get the result and the patches
let patches: Patch[] = [];
let inverse: Patch[] = [];
const result = produce(
    makeObj(), 
    (draft) => {
        draft.push({ B: 2 });
    }, 
    (_patches, _inverse) => {
        patches = _patches;
        inverse = _inverse;
    }
);

// if we apply the patches from the same starting point, we get the same result
const newResult = applyPatches(makeObj(), patches);
expect(newResult).toEqual(result);

// then, if we apply the inverse patches, we obtain the original state
const undone = applyPatches(newResult, inverse);
expect(undone).toEqual(makeObj());

Example with produceWithPatches

The same example can be written more concisely by using produceWithPatches:

typescript
type Make = () => Record<string, number>[]
const makeObj: Make = () => [{ A: 1 }];

// first we get the result and the patches
const [result, patches, inverse] = produceWithPatches(makeObj(), (draft) => {
    draft.push({ B: 2 });
});

// if we apply the patches from the same starting point, we get the same result
const newResult = applyPatches(makeObj(), patches);
expect(newResult).toEqual(result);

// then, if we apply the inverse patches, we obtain the original state
const undone = applyPatches(newResult, inverse);
expect(undone).toEqual(makeObj());

Apply patches mutatively

Sometimes you may want to apply patches to an object without cloning it. You can do it via applyPatchesMutatively:

typescript
const original = [{ A: 1 }];

enableAutoFreeze(false); // don't freeze or it will be impossible to modify the object

const [result, _patches, inverse] = produceWithPatches(original, (draft) => {
    draft.push({ A: 2 });
});

// this will modify the result itself, mutatively without any cloning
const newResult = applyPatchesMutatively(result, inverse);
expect(result).toBe(newResult);
expect(result).toEqual(original);

Note that this will not work if your producer returns a value, for example

typescript
const original = [{ A: 1 }];

enableAutoFreeze(false); // don't freeze or it will be impossible to modify the object

const [result, _patches, inverse] = produceWithPatches(original, (_draft) => {
    return [{ A: 2 }];
});

// this WILL NOT work mutatively
const newResult = applyPatchesMutatively(result, inverse);
expect(result).not.toBe(newResult);