Prinzhorn / scrollmeister

Open-source JavaScript framework to declaratively build scrolling experiences
https://www.scrollmeister.com/
MIT License
37 stars 5 forks source link

React etc. virtual DOM mismatch #14

Open Prinzhorn opened 6 years ago

Prinzhorn commented 6 years ago

E.g. React renders something like

<el-meister gl-effect>
    <img src={src}>
</el-meister>

then the gl-effect behavior will later append a canvas to the wrapper

<el-meister gl-effect>
    <img src="...">
    <canvas></canvas>
</el-meister>

now if React re-renders, funny things might happen. Note: that the lazyload behavior is affected as well. It will remove data-src and update src. But React might later overwrite them again. React could return false from shouldComponentUpdate, but if the src actually changes, it is lost. Double note: the lazyload behavior is irrelevant for the WYSIWYG though.

The React docs completely ignore the issue and just say you can render custom elements like every other element (https://reactjs.org/docs/web-components.html). But they're completely missing the point that custom elements can and will manipulate their child DOM. Shadow DOM would solve this by not actually changing the DOM visible to React. But shadow DOM cannot be polyfilled and is not an option for us.

There are articles out there trying to solve the issue like this https://www.sitepen.com/blog/2017/08/08/wrapping-web-components-with-react/ . But manually syncing React and the DOM sounds like a bad idea and is error prone. Even more important it is not transparent and requires the React user to know about all of the internals of the custom element, which is crazy.

I was trying to figure out if React has some built in support to mark certain elements as "do not touch" https://github.com/facebook/react/issues/6622 . But so far they are only relevant to server side rendering and not to our case https://reactjs.org/docs/dom-elements.html#suppresshydrationwarning Also this would be react specific and we need a solution for all the libraries.

Prinzhorn commented 6 years ago

Can we "simulate" something like shadow DOM? I mean not really, but like this

<el-meister gl-effect>
    <img src={src}>
    <div data-scrollmeister-shadow />
</el-meister>

Now Scrollmeister can do whatever the f it wants inside data-scrollmeister-shadow, e.g. appending a canvas or the touch overlay. In React we can easily abstract this away and offer an ElMeister component.

<ElMeister gl-effect>
    <img src={src}>
</ElMeister>

...

let {children, ...props} = this.props;

<el-meister {...props}>
    {children}
    <div data-scrollmeister-shadow />
</el-meister>
Prinzhorn commented 6 years ago

<shadow-meister> (with display: contents)

Prinzhorn commented 6 years ago
(with display: contents)

done.

And bonus points for automatically cleaning up children when a behavior is detached (#15). Similar to listen.

The only question is what we do about manipulating attributes. I think touching styles is OK. The bigger issue is data-src and src. But we might as well make the lazy-load behavior configurable. So you can disable the automatic replacement and instead React can listen for events and check el.lazyLoad.loaded prop. This can also be useful for other cases where you don't have a data-src but want to lazily fetch() a resource. Behaviors that need this can then connectTo lazyload.

Prinzhorn commented 6 years ago

22 is related is well. Scrollmeister works great so far for static HTML, but DOM manipulation using React, jQuery or plain setAttribute etc. are not done yet.