-
Notifications
You must be signed in to change notification settings - Fork 49
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
Rendering issues when used with ES6 / prototypes #27
Comments
Hmm not sure at first glance. Would you mind throwing up an example in a repo that I could try? |
Thanks for opening this issue! Ummmm, off the top of my head I have literally no idea what could be causing this - are you using This probably definitely needs deeper research to figure out; would you mind setting up a quick repro so we can plough through it? Thanks! edit: @shama jinx! |
Thanks for willing to help! Sure, we have the repo on GitHub:
|
Thanks for the repo example! Using that, I built a reduced test to try and pinpoint the issue. I replaced import yo from 'yo-yo'
class Plugin {
constructor (core, opts) {
this.core = core
this.opts = opts
}
}
class Dummy extends Plugin {
constructor () {
super({
state: { what: 'strange' }
}, {})
this.strange = yo`<h1>this is strange 1</h1>`
}
render () {
const bla = yo`<h1>this is ${this.core.state.what} 2</h1>`
return yo`
<div class="wow-this-works">
<input type="text" value="hello">
${this.strange}
${bla}
</div>
`
}
}
var dummy = new Dummy()
var root = dummy.render()
document.body.appendChild(root) I'm not sure what the issue is but I don't think this is related to bel, babel or the My guess at the problem is you're running into a timing issue. Where one call to A way to combat these types of timing issues is to use a data down, actions up architecture. Where the state is basically an object that lives at the top and is passed into each plugin upon render. The plugin manipulates the data as need to build the desired HTML element and returns the element. Any time an event or async action needs to occur, an action (could be an event emitter, redux thing or just a callback passed down with the data) is called. The action manipulates the state and then sends the data down again to Then just avoid any types of observables triggering a |
Hey! Thanks so much for digging into this. The problem does not occur if I mount an element directly to the DOM, like in your example. It occurs when the Our system works like this: Core runs all the plugins and keeps the state, as you are describing. It also has actions that modify said state. Each plugin can be installed directly to the DOM element or to other plugin, which is the case when we have a Modal dialog and we want the DragDrop plugin (or Dummy in this example) to be installed to that Modal dialog, and not directly to the DOM. The reason Have you witnessed the |
When I ran the build, the tag didn't matter. It started as a |
Sorry, could you pull now, this makes it “work”: transloadit/uppy@9a2d81f Same tags show up, different don’t. |
Ah yeah, now it happens for me. It appears random though. I tried with other tags, some work and some don't. It's highly unlikely it's related to the specific tags though. It renders just fine using Babel and yo-yo on their own within your build system, just not when integrated into the Plugin system. My guess is still timing issues with the architecture. Upon init, it renders on The architecture I am describing is different. Data down, actions up looks like a tree. You pass data to a parent. The parent uses that data to create children and continues passing it further and further down creating a tree. Child elements do not talk to other child elements. They send an action up to the parent to either pass further up or handle itself. They talk through actions manipulating the data and rendering it back down. A core plugin manager is not needed, IMO. As plugins, each element you create has a peer dependency to that core plugin manager. Modifying the API of that framework means all your elements break until you update them. Also those elements will be unusable to me and the larger community.
Or even more simply: import yo from 'yo-yo'
import dummyElement from './dummy.js'
export default function modalElement (state) {
return yo`<div class="modal">
${dummyElement(state)}
</div>`
} Or make the modal a more generic element: import yo from 'yo-yo'
export default function modalElement (contents) {
return yo`<div class="modal">
${contents}
</div>`
} import yo from 'yo-yo'
import modalElement from './modal.js'
import dummyElement from './dummy.js'
let element = modalElement(dummyElement()) I'm not saying any path is right or wrong but this is a path of least resistance with |
Not so random, I too tried with different tags, it’s not the tag that matters, but whether or not I use two of the same. It renders all the time with
That was my experience too. I’ve managed to solve the issue by passing just the render function and not the pre-rendered element to the target, and binding The “two same or different elements” still remains a mystery to me, but I do agree it’s probably the timing / rendering issue. Maybe at some point it was quicker to render two of the same elements? In console it always showed the element though, never undefined.
Thanks for all the examples and explanations again. I do understand what you are saying and will re-read a few times to make sure it sinks in, really appreciated. What you are describing is the general usage pattern for React, Virtual Dom and Yo-Yo, as far as I can tell by exploring a bunch of applications, like WebTorrent Desktop, Chatwizard, your csv-viewer/fs-explorer and others. For Uppy though, we planned for the end user to be able to use it as a ready app, without having to combine components themselves, like so: import Uppy from 'Core.js'
import Dummy from 'Dummy.js'
import Modal from 'Modal.js'
import DragDrop from 'DragDrop.js'
import GoogleDrive from 'GoogleDrive.js'
import ProgressBar from 'ProgressBar.js'
const uppy = new Uppy({debug: true, autoProceed: false})
.use(Modal, {trigger: '#uppyModalOpener'})
.use(ProgressBar, {target: 'body'})
.use(DragDrop, {target: Modal})
.use(GoogleDrive, {target: Modal, host: 'http://server.uppy.io'})
.use(Dummy, {target: Modal})
.run() Or even a pre-bundled version with a script tag: <button id="uppyModalOpener">Open Modal</button>
<link href="../dist/uppy.min.css" rel="stylesheet">
<script src="../dist/uppy.min.js"></script>
<script src="../dist/locales/ru_RU.min.js"></script>
<script>
var uppy = new Uppy.Core({locales: Uppy.locales.ru_RU, debug: true})
.use(Uppy.plugins.Modal, {trigger: '#uppyModalOpener'})
.use(Uppy.plugins.Dummy, {target: Uppy.plugins.Modal})
.run();
</script> In here all of these components share the same state, and all of them are re-rendered when any of that state changes, but they do not always have the parent --> child relationship. Because we thought maybe you want to install Modal to body, DragDrop & GoogleDrive to Modal, and then ProgressBar to the body again. Or more simply: the user can choose to either use the Modal and install everything to it, or just install everything to some dom element. It is more complicated and error-prone, as we have seen, but I am not sure if we should make the user re-create the app himself, and just provide the components. Maybe it makes sense to supply the bundled version for those who want it simple, and then doing the component-way you are describing for those who want more control. The other issue is that our plugins are not just views, they have logic like Google Drive authentication, and since it’s a plugin, this logic can’t be bundled into the core/root component, which is usually the case with this types of architecture. So plugins need to extend |
Thanks again for all your help, closing for now. |
Hey! Using
yo-yo
in a project with ES6 classes: we have Core and Plugins, and each plugin is inheriting from a Plugin class, which serves a boilerplate. Usingsuper
andextend
keywords, which is relevant, because the issue goes away without those. Everything gets transpiled down to ES5 with Babel.Sometimes
yo-yo
won’t render an element that was created in aconstructor
withthis
, like so:<h1>this is strange 1</h1>
gets rendered while<h2>this is strange 2</h2>
does not. If you movethis.strange
from the constructor to render function, it works. If youconsole.log
the elements, they always show up in the console. Also, it works if you wrapyo
call in a function, likethis.strange = function () { return yo'<h1>this is strange 1</h1>' }
And I would think it’s totally
this
andclass
issues, except — wait for it — when you replaceh2
withh1
, it works. Different elements won’t render, but if two of the same are used — it works.Here is a diff of two transpiled examples: https://www.diffchecker.com/ngpc5whl (the one on the left works, the one on the right does not), and are screenshots:
I thought maybe someone who wrote
bel
and knows internals will understand what’s going on here. Where should we even look? Sorry for complicated explanation and thanks in advance.The text was updated successfully, but these errors were encountered: