Skip to content

Commit 5daec17

Browse files
committed
perf: check for state diff before firing :stateChange
1 parent aabb780 commit 5daec17

File tree

1 file changed

+24
-1
lines changed

1 file changed

+24
-1
lines changed

packages/base-controller/src/BaseControllerV2.ts

+24-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type { Json, PublicInterface } from '@metamask/utils';
22
import { enablePatches, produceWithPatches, applyPatches, freeze } from 'immer';
33
import type { Draft, Patch } from 'immer';
4+
import { isEqual, get as getValueFromPath } from 'lodash';
45

56
import type { ActionConstraint, EventConstraint } from './Messenger';
67
import type {
@@ -244,6 +245,28 @@ export class BaseController<
244245
);
245246
}
246247

248+
/**
249+
* Checks if the list of patches would produce the same state (current state).
250+
*
251+
* @param patches - List of state patches.
252+
* @returns True if the patches would produce the same state, false otherwise.
253+
*/
254+
#isSameState(patches: Patch[]) {
255+
for (const { op, path, value: newValue } of patches) {
256+
if (op === 'replace') {
257+
const oldValue = getValueFromPath(this.state, path);
258+
259+
if (!isEqual(oldValue, newValue)) {
260+
return false;
261+
}
262+
} else {
263+
// Any other operation would really update the state (removing/adding).
264+
return false;
265+
}
266+
}
267+
return true;
268+
}
269+
247270
/**
248271
* Updates controller state. Accepts a callback that is passed a draft copy
249272
* of the controller state. If a value is returned, it is set as the new
@@ -272,7 +295,7 @@ export class BaseController<
272295
)(this.#internalState, callback);
273296

274297
// Protect against unnecessary state updates when there is no state diff.
275-
if (patches.length > 0) {
298+
if (patches.length > 0 && !this.#isSameState(patches)) {
276299
this.#internalState = nextState;
277300
this.messagingSystem.publish(
278301
`${this.name}:stateChange`,

0 commit comments

Comments
 (0)