diff --git a/packages/base-controller/package.json b/packages/base-controller/package.json index 86f5b5a6d5c..82864dd6cb2 100644 --- a/packages/base-controller/package.json +++ b/packages/base-controller/package.json @@ -47,7 +47,8 @@ }, "dependencies": { "@metamask/utils": "^11.2.0", - "immer": "^9.0.6" + "immer": "^9.0.6", + "lodash": "^4.17.21" }, "devDependencies": { "@metamask/auto-changelog": "^3.4.4", diff --git a/packages/base-controller/src/BaseControllerV2.ts b/packages/base-controller/src/BaseControllerV2.ts index 8e3b8cdc2d6..76722abf17b 100644 --- a/packages/base-controller/src/BaseControllerV2.ts +++ b/packages/base-controller/src/BaseControllerV2.ts @@ -1,6 +1,7 @@ import type { Json, PublicInterface } from '@metamask/utils'; import { enablePatches, produceWithPatches, applyPatches, freeze } from 'immer'; import type { Draft, Patch } from 'immer'; +import { isEqual, get as getValueFromPath } from 'lodash'; import type { ActionConstraint, EventConstraint } from './Messenger'; import type { @@ -244,6 +245,28 @@ export class BaseController< ); } + /** + * Checks if the list of patches would produce the same state (current state). + * + * @param patches - List of state patches. + * @returns True if the patches would produce the same state, false otherwise. + */ + #isSameState(patches: Patch[]) { + for (const { op, path, value: newValue } of patches) { + if (op === 'replace') { + const oldValue = getValueFromPath(this.state, path); + + if (!isEqual(oldValue, newValue)) { + return false; + } + } else { + // Any other operation would really update the state (removing/adding). + return false; + } + } + return true; + } + /** * Updates controller state. Accepts a callback that is passed a draft copy * of the controller state. If a value is returned, it is set as the new @@ -272,7 +295,7 @@ export class BaseController< )(this.#internalState, callback); // Protect against unnecessary state updates when there is no state diff. - if (patches.length > 0) { + if (patches.length > 0 && !this.#isSameState(patches)) { this.#internalState = nextState; this.messagingSystem.publish( `${this.name}:stateChange`, diff --git a/yarn.lock b/yarn.lock index 746be0fcf46..fd9957a3264 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2681,6 +2681,7 @@ __metadata: deepmerge: "npm:^4.2.2" immer: "npm:^9.0.6" jest: "npm:^27.5.1" + lodash: "npm:^4.17.21" sinon: "npm:^9.2.4" ts-jest: "npm:^27.1.4" typedoc: "npm:^0.24.8"