mobx 7.0.1 → 7.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx",
3
- "version": "7.0.1",
3
+ "version": "7.0.2",
4
4
  "description": "Simple, scalable state management.",
5
5
  "source": "src/mobx.ts",
6
6
  "type": "commonjs",
@@ -265,17 +265,42 @@ export class ObservableSet<T = any> implements Set<T>, IInterceptable<ISetWillCh
265
265
  other = new Set(other)
266
266
  }
267
267
 
268
- transaction(() => {
269
- if (Array.isArray(other)) {
270
- this.clear()
271
- other.forEach(value => this.add(value))
272
- } else if (isES6Set(other)) {
273
- this.clear()
274
- other.forEach(value => this.add(value))
275
- } else if (other !== null && other !== undefined) {
276
- die(41, other)
277
- }
278
- })
268
+ if (Array.isArray(other) || isES6Set(other)) {
269
+ // Only emit `delete`/`add` events (and `reportChanged`) for values that
270
+ // actually change, instead of clearing and re-adding everything. `add` and
271
+ // `delete` are already no-ops for values that are respectively already
272
+ // present or already absent, so we just need to avoid deleting values that
273
+ // are part of the replacement. See #3761.
274
+ transaction(() => {
275
+ // Collect the desired values for quick lookup. `other` is already a Set
276
+ // here when it was passed (or snapshotted from an observable set) as one,
277
+ // so reuse it rather than allocating another; arrays are wrapped (which
278
+ // also dedupes them).
279
+ const replacementValues: Set<T> = isES6Set(other)
280
+ ? other
281
+ : new Set<T>(other as Iterable<T>)
282
+ // Short-circuit the trivial cases: an empty replacement is just a clear,
283
+ // and replacing into an empty set only needs the adds.
284
+ if (replacementValues.size === 0) {
285
+ this.clear()
286
+ return
287
+ }
288
+ if (this.data_.size === 0) {
289
+ replacementValues.forEach(value => this.add(value))
290
+ return
291
+ }
292
+ // Delete values that are not part of the replacement.
293
+ for (const value of this.data_.values()) {
294
+ if (!replacementValues.has(this.dehanceValue_(value))) {
295
+ this.delete(value)
296
+ }
297
+ }
298
+ // Add new values; values that are already present are a no-op.
299
+ replacementValues.forEach(value => this.add(value))
300
+ })
301
+ } else if (other !== null && other !== undefined) {
302
+ die(41, other)
303
+ }
279
304
 
280
305
  return this
281
306
  }