diff --git a/src/client/packages/idom-client-react/src/utils.js b/src/client/packages/idom-client-react/src/utils.js index 09a4200c2..34433b83a 100644 --- a/src/client/packages/idom-client-react/src/utils.js +++ b/src/client/packages/idom-client-react/src/utils.js @@ -2,31 +2,50 @@ import React from "react"; import jsonpatch from "fast-json-patch"; export function useJsonPatchCallback(initial) { - const model = React.useRef(initial); + const doc = React.useRef(initial); const forceUpdate = useForceUpdate(); const applyPatch = React.useCallback( - (pathPrefix, patch) => { - if (pathPrefix) { - patch = patch.map((op) => - Object.assign({}, op, { path: pathPrefix + op.path }) - ); + (path, patch) => { + if (!path) { + // We CANNOT mutate the part of the document because React checks some + // attributes of the model (e.g. model.attributes.style is checked for + // identity). + doc.current = applyNonMutativePatch(doc, patch, false, false, true); + } else { + // We CAN mutate the document here though because we know that nothing above + // The patch `path` is changing. Thus, maintaining the identity for that section + // of the model is accurate. + applyMutativePatch(doc.current, [ + { + op: "replace", + path: path, + // We CANNOT mutate the part of the document where the actual patch is being + // applied. Instead we create a copy because React checks some attributes of + // the model (e.g. model.attributes.style is checked for identity). The part + // of the document above the `path` can be mutated though because we know it + // has not changed. + value: applyNonMutativePatch( + jsonpatch.getValueByPointer(doc.current, path), + patch + ), + }, + ]); } - // Always return a newDocument because React checks some attributes of the model - // (e.g. model.attributes.style is checked for identity) - model.current = jsonpatch.applyPatch( - model.current, - patch, - false, - false, - true - ).newDocument; forceUpdate(); }, - [model] + [doc] ); - return [model.current, applyPatch]; + return [doc.current, applyPatch]; +} + +function applyNonMutativePatch(doc, patch) { + return jsonpatch.applyPatch(doc, patch, false, false, true).newDocument; +} + +function applyMutativePatch(doc, patch) { + jsonpatch.applyPatch(doc, patch, false, true, true).newDocument; } function useForceUpdate() {