8000 Fix observations by stephencookdev · Pull Request #101 · reactive-elements/reactive-elements · GitHub
[go: up one dir, main page]

Skip to content

Conversation

@stephencookdev
Copy link
Contributor

Currently we call getChildren on both the creation, and change observation of nodes.

Unfortunately, getChildren is actually destructive, as it pulls child elements into a document fragment, and DOM nodes must be unique - causing a mutation of the underlying React container, causing React to get super upset and exploding.

This PR simply does not recreate the document fragments on observed changes. Since the document fragment itself is just a reference to the children, then it should be fine to keep a hold to the same fragment forever (regardless of changes to the underlying children)

The only case this wouldn't be true is if someone were to attempt adding new nodes to the child list outside of React. But I can't see that this would ever be a good idea (rather than using React for this), nor can I see any way of making React not be hugely upset by this (given that you would be effectively changing React's rendered output manually from outside React, which React does get super upset about)

@stephencookdev
Copy link
Contributor Author

cc @jackfranklin @ChristianMurphy @PixelsCommander since you all chimed into the previous PR #100

const observer = new MutationObserver(() => {
ReactDOM.unmountComponentAtNode(getRenderRoot(self, useShadowDom));
const props = utils.getProps(self);
props.children = utils.getChildren(self);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get rid of utils.getChildren now ? :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it's used in the initial render (where we do want it to be destructive, as we're about to move the DOM elements from the current DOM node they're in, to React for it to render)

I think very possibly getChildren as a name could stand to be improved, though, to make its destructive nature a bit clearer. Open to naming suggestions there, though

@jackfranklin
Copy link
Collaborator

@stephencookdev this is a really smart solution! I'll let @ChristianMurphy chime in but to me this looks 👌

// as this will cause a mutation in the current React component (because
// of the way that DOM nodes must be unique)
// but the fragment is a reference, so this is fine to keep a hold of
props.children = this.childrenFragment;
Copy link
Member
@ChristianMurphy ChristianMurphy Mar 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The mutation observer also watches for changes to the slot/children/subtree.
Caching the childrenFragment will prevent updates that add children to the slot from being rendered.

@ChristianMurphy
Copy link
Member

The only case this wouldn't be true is if someone were to attempt adding new nodes to the child list outside of React. But I can't see that this would ever be a good idea (rather than using React for this)

🤨 The point of wrapping a React components as a web component is to be able to use it outside React.
So yes, components may get mutated by libraries that aren't React.

@jackfranklin
Copy link
Collaborator

The point of wrapping a React components as a web component is to be able to use it outside React.

I'm a bit confused by this comment. The point of React is that it owns the DOM and rendering your components into it.

@ChristianMurphy can you maybe show an example of when you need it to work with some outside library manipulating a component? We're struggling to understand because we don't do that, we use this library to render React components as web elements. All the state/DOM logic/etc is contained within those React components.

@stephencookdev
Copy link
Contributor Author

So yes, components may get mutated by libraries that aren't React.

On a similar note, I recently made this shadow-root feature optional - but before my change to make it optional, then all children of the web component would be rendered into a shadow DOM.

This means that, before my change, component children would have been entirely hidden from other libraries. I'm unclear as to how my change here is less permissive than that?

@ChristianMurphy
Copy link
Member

This means that, before my change, component children would have been entirely hidden from other libraries. I'm unclear as to how my change here is less permissive than that?

Children get rendered into the shadow DOM, but content can still be updated through the slot.

The point of React is that it owns the DOM and rendering your components into it.

That is the point of React.
The point of wrapping React in a web component, is to make that piece of content agnostic of React.
The page or single page app doesn't need to know what framework is rendering the element, only that it is a custom element.

can you maybe show an example of when you need it to work with some outside library manipulating a component? We're struggling to understand because we don't do that, we use this library to render React components as web elements. All the state/DOM logic/etc is contained within those React components.

TL;DR
the use case are

  1. leveraging React components with slots inside a single page app
  2. keeping the wrapper inline with the capabilities of Vue and Angular

Long version:
uPortal is migrating to use web components for all ui routing and layout.
https://github.com/Jasig/uPortal
https://github.com/uPortal-contrib/uPortal-web-components
an initial draft of layout support can be seen at https://github.com/ChristianMurphy/webcomponent-layout-demo
The router and layout components have the option of adding and modifying slot content in the components dynamically.

The second motivation for wanting this feature, is keeping inline with the web component capabilities of Angular and Vue.
uPortal is a community project, many components are being written by other organizations, and in various technologies (with web components being the common target).
I can't anticipate all of the use cases that the library that will be used for.
I don't want to be put in a situation where slot changes are being done, but don't work in React as they would/should in Angular/Vue, leaving myself or another team member to explain that the author of the component should rewrite their entire component in a different framework because React/Reactive Elements can't do that.

@stephencookdev
Copy link
Contributor Author

(closing this, since there's no plan for merging this, as per comment thread)

@stephencookdev stephencookdev deleted the feature/observer-fix branch September 3, 2019 10:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

0