fix: React selectors should work after unmount (#22750)

Fixes #22729
This commit is contained in:
Alexander Sorokin 2023-05-10 18:18:01 +05:00 committed by GitHub
parent 3b2d247c74
commit cbf4f39957
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 42 additions and 13 deletions

View File

@ -147,16 +147,20 @@ function findReactRoots(root: Document | ShadowRoot, roots: ReactVNode[] = []):
do {
const node = walker.currentNode;
const reactNode = node as { readonly [customKey: string]: any };
// React 17+
// React sets rootKey when mounting
// @see https://github.com/facebook/react/blob/a724a3b578dce77d427bef313102a4d0e978d9b4/packages/react-dom/src/client/ReactDOMComponentTree.js#L62-L64
const rootKey = Object.keys(node).find(key => key.startsWith('__reactContainer'));
const rootKey = Object.keys(reactNode).find(key => key.startsWith('__reactContainer') && reactNode[key] !== null);
if (rootKey) {
roots.push((node as any)[rootKey].stateNode.current);
} else if (node.hasOwnProperty('_reactRootContainer')) {
// ReactDOM Legacy client API:
// @see https://github.com/baruchvlz/resq/blob/5c15a5e04d3f7174087248f5a158c3d6dcc1ec72/src/utils.js#L329
roots.push((node as any)._reactRootContainer._internalRoot.current);
roots.push(reactNode[rootKey].stateNode.current);
} else {
const legacyRootKey = '_reactRootContainer';
if (reactNode.hasOwnProperty(legacyRootKey) && reactNode[legacyRootKey] !== null) {
// ReactDOM Legacy client API:
// @see https://github.com/baruchvlz/resq/blob/5c15a5e04d3f7174087248f5a158c3d6dcc1ec72/src/utils.js#L329
roots.push(reactNode[legacyRootKey]._internalRoot.current);
}
}
// Pre-react 16: rely on `data-reactroot`

View File

@ -102,8 +102,12 @@ class App extends React.Component {
}
}
window.mountApp = element => ReactDOM.render(e(App, null, null), element);
window.mountApp = element => {
const root = ReactDOM.render(e(App, null, null), element);
const unmount = () => ReactDOM.unmountComponentAtNode(element);
return {root, unmount};
};
window.app = window.mountApp(document.getElementById('root'));
window.mountNestedApp = () => window.mountApp(window.app.refs.mountPoint);
window.mountNestedApp = () => window.mountApp(window.app.root.refs.mountPoint);
</script>

View File

@ -50,7 +50,7 @@ class NewBook extends React.Component {
}
render() {
return e(React.Fragment, null,
return e(React.Fragment, null,
e('input', {onInput: this.onInput.bind(this)}, null),
e('button', {
onClick: () => this.props.onNewBook(this.state),
@ -101,9 +101,13 @@ class App extends React.Component {
}
}
window.mountApp = element => ReactDOM.render(e(App, null, null), element);
window.mountApp = element => {
const root = ReactDOM.render(e(App, null, null), element);
const unmount = () => ReactDOM.unmountComponentAtNode(element);
return {root, unmount};
};
window.app = window.mountApp(document.getElementById('root'));
window.mountNestedApp = () => window.mountApp(window.app.mountPoint.current);
window.mountNestedApp = () => window.mountApp(window.app.root.mountPoint.current);
</script>

View File

@ -95,8 +95,12 @@ class App extends React.Component {
}
}
window.mountApp = element => ReactDOM.render(e(App, null, null), element);
window.mountApp = element => {
const root = ReactDOM.render(e(App, null, null), element);
const unmount = () => ReactDOM.unmountComponentAtNode(element);
return {root, unmount};
};
window.app = window.mountApp(document.getElementById('root'));
window.mountNestedApp = () => window.mountApp(window.app.mountPoint.current);
window.mountNestedApp = () => window.mountApp(window.app.root.mountPoint.current);
</script>

View File

@ -156,6 +156,19 @@ for (const [name, url] of Object.entries(reacts)) {
});
await expect(page.locator(`_react=BookItem`)).toHaveCount(6);
});
it('should work with multiroot react after unmount', async ({ page }) => {
await expect(page.locator(`_react=BookItem`)).toHaveCount(3);
await page.evaluate(() => {
const anotherRoot = document.createElement('div');
document.body.append(anotherRoot);
// @ts-ignore
const newRoot = window.mountApp(anotherRoot);
newRoot.unmount();
});
await expect(page.locator(`_react=BookItem`)).toHaveCount(3);
});
});
}