You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* clearer error when plugin is missing
* chore: fix travis build not failing, fixes#688 (?)
* fix: make plugin loading idempotent, fixes#692
* docs: Organize performance and pitfalls, and document nested produce behavior. Fixes#694
* chore: fix prod-build-issues
exports[`applyPatches throws when \`op\` is not "add", "replace", nor "remove" 1`] =`"[Immer] minified error nr: 17 copy. Find the full error at: https://bit.ly/3cXEKWf"`;
3
+
exports[`applyPatches throws when \`op\` is not "add", "replace", nor "remove" 1`] =`"[Immer] minified error nr: 17 'copy'. Find the full error at: https://bit.ly/3cXEKWf"`;
4
4
5
-
exports[`applyPatches throws when \`path\` cannot be resolved 1`] =`"[Immer] minified error nr: 15 a/b. Find the full error at: https://bit.ly/3cXEKWf"`;
5
+
exports[`applyPatches throws when \`path\` cannot be resolved 1`] =`"[Immer] minified error nr: 15 'a/b'. Find the full error at: https://bit.ly/3cXEKWf"`;
6
6
7
-
exports[`applyPatches throws when \`path\` cannot be resolved 2`] =`"[Immer] minified error nr: 15 a/b/c. Find the full error at: https://bit.ly/3cXEKWf"`;
7
+
exports[`applyPatches throws when \`path\` cannot be resolved 2`] =`"[Immer] minified error nr: 15 'a/b/c'. Find the full error at: https://bit.ly/3cXEKWf"`;
exports[`ES5 plugins should throw if no proxies are available error when using ES5 1`] =`"[Immer] minified error nr: 19 ES5. Find the full error at: https://bit.ly/3cXEKWf"`;
3
+
exports[`ES5 plugins should throw if no proxies are available error when using ES5 1`] =`"[Immer] minified error nr: 18 'ES5'. Find the full error at: https://bit.ly/3cXEKWf"`;
4
4
5
-
exports[`error when using Maps 1`] =`"[Immer] minified error nr: 19 MapSet. Find the full error at: https://bit.ly/3cXEKWf"`;
5
+
exports[`error when using Maps 1`] =`"[Immer] minified error nr: 18 'MapSet'. Find the full error at: https://bit.ly/3cXEKWf"`;
6
6
7
-
exports[`error when using patches - 1 1`] =`"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`;
7
+
exports[`error when using patches - 1 1`] =`"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`;
8
8
9
-
exports[`error when using patches - 2 1`] =`"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`;
9
+
exports[`error when using patches - 2 1`] =`"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`;
10
10
11
-
exports[`error when using patches - 3 1`] =`"[Immer] minified error nr: 19 Patches. Find the full error at: https://bit.ly/3cXEKWf"`;
11
+
exports[`error when using patches - 3 1`] =`"[Immer] minified error nr: 18 'Patches'. Find the full error at: https://bit.ly/3cXEKWf"`;
Copy file name to clipboardexpand all lines: docs/performance.md
+15-3
Original file line number
Diff line number
Diff line change
@@ -42,6 +42,18 @@ Most important observation:
42
42
43
43
## Performance tips
44
44
45
-
- When adding a large data set to the state tree in an Immer producer (for example data received from a JSON endpoint), it is worth to call `Object.freeze(json)` on the root of the data to be added first. This will allow Immer to add the new data to the tree faster, as it will skip freezing it, or searching the tree for any changes (drafts) that might be made.
46
-
- Immer will convert anything you read in a draft recursively into a draft as well. If you have expensive side effect free operations on a draft that involves a lot of reading, for example finding an index using `find(Index)` in a very large array, you can speed this up by first doing the search, and only call the `produce` function once you know the index. Thereby preventing Immer to turn everything that was searched for in a draft. Or, perform the search on the original value of a draft, by using `original(someDraft)`, which boils to the same thing.
47
-
- Always try to pull produce 'up', for example `for (let x of y) produce(base, d => d.push(x))` is exponentially slower than `produce(base, d => { for (let x of y) d.push(x)})`
45
+
### Pre-freeze data
46
+
47
+
When adding a large data set to the state tree in an Immer producer (for example data received from a JSON endpoint), it is worth to call `Object.freeze(json)` on the root of the data to be added first. This will allow Immer to add the new data to the tree faster, as it will skip freezing it, or searching the tree for any changes (drafts) that might be made.
48
+
49
+
### You can always opt-out
50
+
51
+
Realize that immer is opt-in everywhere, so it is perfectly fine to manually write super performance critical reducers, and use immer for all the normal ones. Even from within a producer you opt-out from Immer for certain parts of your logic by using utilies `original` or `current` and perform some of your operations on plain JavaScript objects.
52
+
53
+
### For expensive search operations, read from the original state, not the draft
54
+
55
+
Immer will convert anything you read in a draft recursively into a draft as well. If you have expensive side effect free operations on a draft that involves a lot of reading, for example finding an index using `find(Index)` in a very large array, you can speed this up by first doing the search, and only call the `produce` function once you know the index. Thereby preventing Immer to turn everything that was searched for in a draft. Or, alternatively, perform the search on the original value of a draft, by using `original(someDraft)`, which boils to the same thing.
56
+
57
+
### Pull produce as far up as possible
58
+
59
+
Always try to pull produce 'up', for example `for (let x of y) produce(base, d => d.push(x))` is exponentially slower than `produce(base, d => { for (let x of y) d.push(x)})`
1. For performance tips, see [Performance Tips](https://immerjs.github.io/immer/docs/performance/#performance-tips).
11
-
1. Don't redefine draft like, `draft = myCoolNewState`. Instead, either modify the `draft` or return a new state. See [Returning data from producers](https://immerjs.github.io/immer/docs/return).
12
-
1. Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, and there should be no circular references.
13
-
1. Since Immer uses proxies, reading huge amounts of data from state comes with an overhead (especially in the ES5 implementation). If this ever becomes an issue (measure before you optimize!), do the current state analysis before entering the producer function or read from the `currentState` rather than the `draftState`. Also, realize that immer is opt-in everywhere, so it is perfectly fine to manually write super performance critical reducers, and use immer for all the normal ones. Also note that `original` can be used to get the original state of an object, which is cheaper to read.
14
-
1. It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer.
15
-
1. Immer [does not support exotic objects](https://github.com/immerjs/immer/issues/504) such as window.location.
16
-
1. You will need to enable your own classes to work properly with Immer. For docs on the topic, check out the section on [working with complex objects](https://immerjs.github.io/immer/docs/complex-objects).
17
-
1. For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays.
18
-
1. Note that data that comes from the closure, and not from the base state, will never be drafted, even when the data has become part of the new draft:
19
-
1. The set of patches generated by Immer should be correct, that is, applying them to an equal base object should result in the same end state. However Immer does not guarantee the generated set of patches will be optimal, that is, the minimum set of patches possible.
10
+
### Performance tipes
11
+
12
+
For performance tips, see [Performance Tips](https://immerjs.github.io/immer/docs/performance/#performance-tips).
13
+
14
+
### Don't reassign the recipe argument
15
+
16
+
Never reassign the `draft` argument (example: `draft = myCoolNewState`). Instead, either modify the `draft` or return a new state. See [Returning data from producers](https://immerjs.github.io/immer/docs/return).
17
+
18
+
### Immer only supports unidirectional trees
19
+
20
+
Immer assumes your state to be a unidirectional tree. That is, no object should appear twice in the tree, there should be no circular references. There should be exactly one path from the root to any node of the tree.
21
+
22
+
### Never explicitly return `undefined` from a producer
23
+
24
+
It is possible to return values from producers, except, it is not possible to return `undefined` that way, as it is indistinguishable from not updating the draft at all! If you want to replace the draft with `undefined`, just return `nothing` from the producer.
25
+
26
+
### Don't mutate exotic objects
27
+
28
+
Immer [does not support exotic objects](https://github.com/immerjs/immer/issues/504) such as window.location.
29
+
30
+
### Classes should be made draftable or not mutated
31
+
32
+
You will need to enable your own classes to work properly with Immer. For docs on the topic, check out the section on [working with complex objects](https://immerjs.github.io/immer/docs/complex-objects).
33
+
34
+
### Only valid indices and length can be mutated on Arrays
35
+
36
+
For arrays, only numeric properties and the `length` property can be mutated. Custom properties are not preserved on arrays.
37
+
38
+
### Data not originating from the state will never be drafted
39
+
40
+
Note that data that comes from the closure, and not from the base state, will never be drafted, even when the data has become part of the new draft.
20
41
21
42
```javascript
22
43
functiononReceiveTodo(todo) {
@@ -33,3 +54,19 @@ function onReceiveTodo(todo) {
33
54
})
34
55
}
35
56
```
57
+
58
+
### Immer patches are not necessarily optimal
59
+
60
+
The set of patches generated by Immer should be correct, that is, applying them to an equal base object should result in the same end state. However Immer does not guarantee the generated set of patches will be optimal, that is, the minimum set of patches possible.
61
+
62
+
### Always use the result of nested producers
63
+
64
+
Nested `produce` calls are supported, but note that `produce` will _always_ produce a new state, so even when passing a draft to a nested produce, the changes made by the inner produce won't be visibile in the draft that was passed it, but only in the output that is produced. In other words, when using nested produce, you get a draft of a draft and the result of the inner produce should be merged back into the original draft (or returned). For example `produce(state, draft => { produce(draft.user, userDraft => { userDraft.name += "!" })})` won't work as the output if the inner produce isn't used. The correct way to use nested producers is:
Copy file name to clipboardexpand all lines: src/utils/errors.ts
+1-2
Original file line number
Diff line number
Diff line change
@@ -29,7 +29,6 @@ const errors = {
29
29
18(plugin: string){
30
30
return`The plugin for '${plugin}' has not been loaded into Immer. To enable the plugin, import and call \`enable${plugin}()\` when initializing your application.`
31
31
},
32
-
19: "plugin not loaded",
33
32
20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available",
34
33
21(thing: string){
35
34
return`produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '${thing}'`
@@ -54,7 +53,7 @@ export function die(error: keyof typeof errors, ...args: any[]): never {
0 commit comments