-
Notifications
You must be signed in to change notification settings - Fork 47k
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
[Fiber][Float] Error when a host fiber changes "flavor" #29693
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 | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,123 @@ | ||||||
/** | ||||||
* Copyright (c) Meta Platforms, Inc. and affiliates. | ||||||
* | ||||||
* This source code is licensed under the MIT license found in the | ||||||
* LICENSE file in the root directory of this source tree. | ||||||
* | ||||||
* @emails react-core | ||||||
* @jest-environment ./scripts/jest/ReactDOMServerIntegrationEnvironment | ||||||
*/ | ||||||
|
||||||
'use strict'; | ||||||
|
||||||
let JSDOM; | ||||||
let React; | ||||||
let ReactDOMClient; | ||||||
let container; | ||||||
let waitForAll; | ||||||
|
||||||
describe('ReactDOM HostSingleton', () => { | ||||||
beforeEach(() => { | ||||||
jest.resetModules(); | ||||||
JSDOM = require('jsdom').JSDOM; | ||||||
// Test Environment | ||||||
const jsdom = new JSDOM( | ||||||
'<!DOCTYPE html><html><head></head><body><div id="container">', | ||||||
{ | ||||||
runScripts: 'dangerously', | ||||||
}, | ||||||
); | ||||||
global.window = jsdom.window; | ||||||
global.document = jsdom.window.document; | ||||||
container = global.document.getElementById('container'); | ||||||
|
||||||
React = require('react'); | ||||||
ReactDOMClient = require('react-dom/client'); | ||||||
|
||||||
const InternalTestUtils = require('internal-test-utils'); | ||||||
waitForAll = InternalTestUtils.waitForAll; | ||||||
}); | ||||||
|
||||||
it('errors when a hoistable component becomes a Resource', async () => { | ||||||
const errors = []; | ||||||
function onError(e) { | ||||||
errors.push(e.message); | ||||||
} | ||||||
const root = ReactDOMClient.createRoot(container, { | ||||||
onUncaughtError: onError, | ||||||
}); | ||||||
|
||||||
root.render( | ||||||
<div> | ||||||
<link rel="preload" href="bar" as="style" /> | ||||||
</div>, | ||||||
); | ||||||
await waitForAll([]); | ||||||
|
||||||
root.render( | ||||||
<div> | ||||||
<link rel="stylesheet" href="bar" precedence="default" /> | ||||||
</div>, | ||||||
); | ||||||
await waitForAll([]); | ||||||
if (__DEV__) { | ||||||
expect(errors).toEqual([ | ||||||
`Expected <link> not to update to be updated to a stylehsheet with precedence. Check the \`rel\`, \`href\`, and \`precedence\` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key. | ||||||
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.
Suggested change
|
||||||
|
||||||
- <link rel=\"preload\" href=\"bar\" ... /> | ||||||
+ <link rel=\"stylesheet\" href=\"bar\" precedence=\"default\" />`, | ||||||
]); | ||||||
} else { | ||||||
expect(errors).toEqual([ | ||||||
'Expected <link> not to update to be updated to a stylehsheet with precedence. Check the `rel`, `href`, and `precedence` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key.', | ||||||
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.
Suggested change
|
||||||
]); | ||||||
} | ||||||
}); | ||||||
|
||||||
it('errors when a hoistable Resource becomes an instance', async () => { | ||||||
const errors = []; | ||||||
function onError(e) { | ||||||
errors.push(e.message); | ||||||
} | ||||||
const root = ReactDOMClient.createRoot(container, { | ||||||
onUncaughtError: onError, | ||||||
}); | ||||||
|
||||||
root.render( | ||||||
<div> | ||||||
<link rel="stylesheet" href="bar" precedence="default" /> | ||||||
</div>, | ||||||
); | ||||||
await waitForAll([]); | ||||||
const event = new window.Event('load'); | ||||||
const preloads = document.querySelectorAll('link[rel="preload"]'); | ||||||
for (let i = 0; i < preloads.length; i++) { | ||||||
const node = preloads[i]; | ||||||
node.dispatchEvent(event); | ||||||
} | ||||||
const stylesheets = document.querySelectorAll('link[rel="preload"]'); | ||||||
for (let i = 0; i < stylesheets.length; i++) { | ||||||
const node = stylesheets[i]; | ||||||
node.dispatchEvent(event); | ||||||
} | ||||||
|
||||||
root.render( | ||||||
<div> | ||||||
<link rel="foo" href="bar" /> | ||||||
</div>, | ||||||
); | ||||||
await waitForAll([]); | ||||||
if (__DEV__) { | ||||||
expect(errors).toEqual([ | ||||||
`Expected stylesheet with precedence to not be updated to a different kind of <link>. Check the \`rel\`, \`href\`, and \`precedence\` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key. | ||||||
|
||||||
- <link rel=\"stylesheet\" href=\"bar\" precedence=\"default\" /> | ||||||
+ <link rel=\"foo\" href=\"bar\" />`, | ||||||
]); | ||||||
} else { | ||||||
expect(errors).toEqual([ | ||||||
'Expected stylesheet with precedence to not be updated to a different kind of <link>. Check the `rel`, `href`, and `precedence` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key.', | ||||||
]); | ||||||
} | ||||||
}); | ||||||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1052,7 +1052,6 @@ function completeWork( | |
return null; | ||
} else { | ||
// This is a Hoistable Instance | ||
|
||
// This must come at the very end of the complete phase. | ||
bubbleProperties(workInProgress); | ||
gnoff marked this conversation as resolved.
Show resolved
Hide resolved
|
||
preloadInstanceAndSuspendIfNeeded( | ||
|
@@ -1064,32 +1063,34 @@ function completeWork( | |
return null; | ||
} | ||
} else { | ||
// We are updating. | ||
const currentResource = current.memoizedState; | ||
if (nextResource !== currentResource) { | ||
// We are transitioning to, from, or between Hoistable Resources | ||
// and require an update | ||
markUpdate(workInProgress); | ||
} | ||
if (nextResource !== null) { | ||
// This is a Hoistable Resource | ||
// This must come at the very end of the complete phase. | ||
|
||
bubbleProperties(workInProgress); | ||
if (nextResource === currentResource) { | ||
workInProgress.flags &= ~MaySuspendCommit; | ||
} else { | ||
// This is an update. | ||
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. What all this refactoring about? Does it do anything? 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. We no longer support transitioning between types (we error in begin work) so we can simplify the update logic. If we have a resource now then we must have had one before. If we don't have one now we must have not had one before. this removes a duplicate check of nextResource !== currentResource. |
||
if (nextResource) { | ||
// This is a Resource | ||
if (nextResource !== current.memoizedState) { | ||
// we have a new Resource. we need to update | ||
markUpdate(workInProgress); | ||
// This must come at the very end of the complete phase. | ||
bubbleProperties(workInProgress); | ||
// This must come at the very end of the complete phase, because it might | ||
// throw to suspend, and if the resource immediately loads, the work loop | ||
// will resume rendering as if the work-in-progress completed. So it must | ||
// fully complete. | ||
preloadResourceAndSuspendIfNeeded( | ||
workInProgress, | ||
nextResource, | ||
type, | ||
newProps, | ||
renderLanes, | ||
); | ||
return null; | ||
} else { | ||
// This must come at the very end of the complete phase. | ||
bubbleProperties(workInProgress); | ||
workInProgress.flags &= ~MaySuspendCommit; | ||
return null; | ||
} | ||
return null; | ||
} else { | ||
// This is a Hoistable Instance | ||
// This is an Instance | ||
// We may have props to update on the Hoistable instance. | ||
if (supportsMutation) { | ||
const oldProps = current.memoizedProps; | ||
|
@@ -1107,7 +1108,6 @@ function completeWork( | |
renderLanes, | ||
); | ||
} | ||
|
||
// This must come at the very end of the complete phase. | ||
bubbleProperties(workInProgress); | ||
preloadInstanceAndSuspendIfNeeded( | ||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -512,5 +512,7 @@ | |||||
"524": "Values cannot be passed to next() of AsyncIterables passed to Client Components.", | ||||||
"525": "A React Element from an older version of React was rendered. This is not supported. It can happen if:\n- Multiple copies of the \"react\" package is used.\n- A library pre-bundled an old copy of \"react\" or \"react/jsx-runtime\".\n- A compiler tries to \"inline\" JSX instead of using the runtime.", | ||||||
"526": "Could not reference an opaque temporary reference. This is likely due to misconfiguring the temporaryReferences options on the server.", | ||||||
"527": "Incompatible React versions: The \"react\" and \"react-dom\" packages must have the exact same version. Instead got:\n - react: %s\n - react-dom: %s\nLearn more: https://react.dev/warnings/version-mismatch" | ||||||
"527": "Incompatible React versions: The \"react\" and \"react-dom\" packages must have the exact same version. Instead got:\n - react: %s\n - react-dom: %s\nLearn more: https://react.dev/warnings/version-mismatch", | ||||||
"528": "Expected <link> not to update to be updated to a stylehsheet with precedence. Check the `rel`, `href`, and `precedence` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key.%s", | ||||||
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.
Suggested change
|
||||||
"529": "Expected stylesheet with precedence to not be updated to a different kind of <link>. Check the `rel`, `href`, and `precedence` props of this component. Alternatively, check whether two different <link> components render in the same slot or share the same key.%s" | ||||||
} |
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.