Fix: React cannot render in ShadowRoot #15894
Conversation
Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla. If you are contributing on behalf of someone else (eg your employer), the individual CLA may not be sufficient and your employer may need the corporate CLA signed. If you have received this in error or have any questions, please contact us at cla@fb.com. Thanks! |
ReactDOM: size: 0.0%, gzip: -0.0% Details of bundled changes.Comparing: c40075a...3fe3666 react-dom
Generated by |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks! |
Is this going to fix the input event propagation of shadow DOM? |
Let me have a try. And I also need a rebase on the react codebase |
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 822541c:
|
I don't get your meaning by "fix the input event propagation". My change fix the following example. Checkout if it is what you mean <html>
<body>
<script src="../../../build/node_modules/react/umd/react.development.js"></script>
<!-- The latest version on unpkg won't work on this example. -->
<!-- <script src="https://unpkg.com/react-dom/umd/react-dom.development.js"></script> -->
<script src="../../../build/node_modules/react-dom/umd/react-dom.development.js"></script>
<script src="https://unpkg.com/babel-standalone@6/babel.js"></script>
<div id="container"></div>
<custom-element>
#ShadowRoot: open
<div>
<!-- React mount on this node, not the ShadowRoot -->
</div>
</custom-element>
<script>
var s = document.querySelector('custom-element').attachShadow({ mode: 'open' })
var d = document.createElement('div')
s.appendChild(d)
</script>
<script type="text/babel">
function Comp() {
const [s, ss] = React.useState('input')
return <span>{s}<input value={s} onChange={e => ss(e.target.value)} /></span>
}
ReactDOM.render(
<Comp />,
d
);
</script>
</body>
</html> |
This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution. |
Don't close it. |
cc @trueadm on this one |
These changes look promising. Thank you for taking the time to do them. I've added some suggested changes, but it would be great if you could add a Jest test too, so we can ensure this doesn't regress in the future (both for events and |
It seems like jsdom doesn't support shadow dom ( jsdom/jsdom#1030 ) so I can't write a test for that |
Looking at this again, it clashes with some of the work we're looking into now in regards to moving event registration away from the document and to roots instead. I'd rather come back to this PR in the future (if needed). |
fc01ed9 if so, can you cherry pick this commit at least? |
Also Fix the merge conflicts |
In 62861bb#diff-adeaf9850a891faf89a84c73308e0e0bR284 @trueadm mentioned:
Why? Rejecting document fragments will disable the ability that React to render in a ShadowRoot. |
@shivaylamba @trueadm I have rebased this PR. Due to conflict with React new event system, the other 3 commits are removed. Only ce0f272 (fix Again, why throws on document fragments container? It's useful when trying to wrap the React component into a web component. |
This pull request has been automatically marked as stale. If this pull request is still relevant, please leave any comment (for example, "bump"), and we'll keep it open. We are sorry that we haven't been able to prioritize reviewing it yet. Your contribution is very much appreciated. |
Material needs to migrate away from it; we shouldn't be adding more code to support it. |
@trueadm I made a fix, but I'm not sure if this fix covers all usage with ShadowRoot. I have tried the following mounting ways: <!-- Working! -->
#ShadowRoot: open (React Root)
<div onClick={x => console.log(x)}>Content</div>
<!-- Working! -->
#ShadowRoot: open
<div> (React Root)
<div onClick={x => console.log(x)}>Content</div>
</div>
<!-- Working! -->
#ShadowRoot: open (React Portal Root)
<div onClick={x => console.log(x)}>Content</div>
<!-- Working! -->
#ShadowRoot: open
<div> (React Portal Root)
<div onClick={x => console.log(x)}>Content</div>
</div> But I found a strange problem: function App() {
return <h1 onClick={console.log}>h1</h1>;
// console.log is replaced by "disableLog" so I can't log any event.
return <h1 onClick={x => console.log(x)}>h1</h1>;
// have to do this
} |
That's to be expected, React replaces
This looks good and the implementation is how I would have gone about it too. It appears you need to fix some Flow issues. It would also be great to have a test fixture for ShadowRoots, maybe using the above cases as code examples (see https://github.com/facebook/react/tree/master/fixtures/dom/src for reference). Thanks for this :) |
The console replacement part is confusing to me. You can't cancel all side effects after all, this is making people(me) confused and doesn't really resolve the side effect problem. I'll resolve the CI error and add tests later this week! |
This is something we do in DEV to avoid confusion around double renders (to catch side-effects that might cause problems for concurrent mode). Last time I checked, in regards to console.log, we override it so you don't get duplicate console logs. Duplicate console logs might be even more confusing otherwise. |
@trueadm is correct. React only overrides the console methods for the second render, which it only does in DEV. This prevents duplicate logs from being in the console and confusing people. We understand that it might be confusing as well to see a |
Actually, duplicate logs will warn the developer about they shouldn't do side effect things out of |
We test changes like this within Facebook before we deploy them to open source. From early testing experience, duplicate logs confuse developers into thinking that either React or their application code is broken and triggering multiple renderers. Silencing the log before the second render dramatically reduced the reports we got from confused devs. |
It's not resolving the problem. It's hiding the problem. And people (me) start to thinking Another approach might be, if it is not in the rendering stage, let |
I feel like this discussion had gone off track. If you have concerns about the behavior around the |
Discussion about this moved to #19537 |
This PR fixes #19558 By the way: Material-UI is only using findDOMNode for backwards compatibility. If you're not using class components with v4 then everything should be fine. v5 will drop findDOMNode completely. You can test this with |
@trueadm Hi I have fixed the flow type problem, but I have no idea about how to add tests. The basic idea is const x = document.querySelector('#root');
const shadow = x.attachShadow({mode: 'open'});
ReactDOM.unstable_createRoot(shadow).render(<App />);
function App() {
return <h1 onClick={console.trace}>h1</h1>;
} If the click invokes the function, the test passes. |
Apparently we broke this before and it was last fixed in #6462. |
@@ -256,7 +257,7 @@ if (__DEV__) { | |||
} | |||
|
|||
export function ensureListeningTo( | |||
rootContainerInstance: Element | Node, | |||
rootContainerInstance: Element | Node | ShadowRoot, |
gaearon
Aug 17, 2020
Member
This is unnecessary. ShadowRoot
is already a Node
. (So is element, fwiw)
This is unnecessary. ShadowRoot
is already a Node
. (So is element, fwiw)
I pushed a slightly different fix:
|
Btw sorry this took long to review; I misunderstood the report in #19558 and thought this was already broken in 16 because both links led to the same sandbox. |
1287670
into
facebook:master
My bad, sorry. I have to be more careful when forking codesandbox for different React versions |
Haha no problem, I do that all the time |
Fixes #19558
This pr fix:
ReactDOM.findDOMNode not working well with aShadowRoot
node