Appearance
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);