-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix #2192 - dont recycle frozen objects #2193
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -91,7 +91,7 @@ describe('recycleNodesInto', () => { | |
const recycled = recycleNodesInto(prevData, nextData); | ||
|
||
expect(recycled).not.toBe(prevData); | ||
expect(recycled.bar).toBe(prevData.bar); | ||
expect(recycled.foo).toBe(prevData.foo); | ||
}); | ||
|
||
it('recycles identical objects', () => { | ||
|
@@ -112,6 +112,28 @@ describe('recycleNodesInto', () => { | |
expect(recycled).toBe(prevData); | ||
}); | ||
|
||
it('does not mutate frozen equal parent objects with equal leaf objects', () => { | ||
const prevData = {foo: {bar: 1}}; | ||
const nextData = {foo: {bar: 1}}; | ||
Object.freeze(nextData); | ||
Object.freeze(nextData.foo); | ||
const recycled = recycleNodesInto(prevData, nextData); | ||
|
||
expect(recycled).toBe(prevData); | ||
expect(recycled.foo).toBe(prevData.foo); | ||
}); | ||
|
||
it('does not mutate frozen unequal parent objects with equal leaf objects', () => { | ||
const prevData = {foo: {bar: 1}, baz: 2}; | ||
const nextData = {foo: {bar: 1}, baz: 200}; | ||
Object.freeze(nextData); | ||
Object.freeze(nextData.foo); | ||
const recycled = recycleNodesInto(prevData, nextData); | ||
|
||
expect(recycled).not.toBe(prevData); | ||
expect(recycled.foo).not.toBe(prevData.foo); | ||
}); | ||
|
||
it('does not recycle arrays as objects', () => { | ||
const prevData = [1, 2]; | ||
const nextData = {0: 1, 1: 2}; | ||
|
@@ -142,6 +164,13 @@ describe('recycleNodesInto', () => { | |
expect(recycleNodesInto(prevData, nextData)).toBe(prevData); | ||
}); | ||
|
||
it('recycles arrays with equal objects without mutating frozen `nextData`', () => { | ||
const prevData = [{foo: 1}, {bar: 2}]; | ||
const nextData = [{foo: 1}, {bar: 2}]; | ||
Object.freeze(nextData); | ||
expect(recycleNodesInto(prevData, nextData)).toBe(prevData); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we add another test for recycling in objects as well? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure thing! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I went to add the requested test, but it looks like I already had one that does this: So this should be good to go. |
||
|
||
it('recycles arrays without mutating `prevData`', () => { | ||
const prevItem = {foo: 1}; | ||
const prevData = [prevItem]; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test is definitely wrong, correct? If
nextData
is frozen and we don't expect to mutate it, then we would expect.toBe(nextData)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I'm pretty sure this test is correct. It's testing that even if objects are frozen, we still recycle
prevData
object if it is deeply equal tonextData
. This is important for preventing identical renders from triggering react diffing downstream.The behavior of frozen objects differs from mutable ones only in that we can't preserve object equity in unchanged branches of a state tree that does have changes.
Given this
prevData
:we can recycle the entire object if
nextData
looks like this:...whether or not the anything is frozen.
However, if our
nextData
looks like this:...we can recycle B1 from
prevData
, but this requires mutating A2, which crashes the whole app if A2 is frozen. If we were really trying to optimize the frozen object case, it would be possible to create a new A2 to hold theprevData
B1 andnextData
B2, but since this only effects the dev environment, that's optimization that is not worth doing.