1.7 KB56 lines
Blame
1/**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7
8import type {ValueObject} from 'immutable';
9
10const IS_RECORD_SYMBOL = '@@__IMMUTABLE_RECORD__@@';
11
12/** Wraps a ValueObject so it self-updates on equals. */
13export class SelfUpdate<T extends ValueObject> implements ValueObject {
14 inner: T;
15
16 /**
17 * Tell Recoil to not deepFreeze (Object.seal) this object. This is needed
18 * since we might update the `inner` field. We didn't break Recoil
19 * assumptions since we maintain the same "value" of the object.
20 *
21 * See https://github.com/facebookexperimental/Recoil/blob/0.7.7/packages/shared/util/Recoil_deepFreezeValue.js#L42
22 * Recoil tests `value[IS_RECORD_SYMBOL] != null`.
23 *
24 * For immutable.js, it actually checks the boolean value.
25 * See https://github.com/immutable-js/immutable-js/blob/v4.3.4/src/predicates/isRecord.js
26 * Immutable.js uses `Boolean(maybeRecord && maybeRecord[IS_RECORD_SYMBOL])`.
27 *
28 * By using `false`, this tricks Recoil to treat us as an immutable value,
29 * while does not break Immutable.js' type checking.
30 */
31 [IS_RECORD_SYMBOL] = false;
32
33 constructor(inner: T) {
34 this.inner = inner;
35 }
36
37 hashCode(): number {
38 return this.inner.hashCode() + 1;
39 }
40
41 equals(other: unknown): boolean {
42 if (!(other instanceof SelfUpdate)) {
43 return false;
44 }
45 if (this === other) {
46 return true;
47 }
48 const otherInner = other.inner;
49 const result = this.inner.equals(otherInner);
50 if (result && this.inner !== otherInner) {
51 this.inner = otherInner;
52 }
53 return result;
54 }
55}
56