Skip to content
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

Add testing for 'idiomatic way to pass child component lists into an element's constructor' #78

Open
pdf opened this issue Jan 26, 2017 · 10 comments
Milestone

Comments

@pdf
Copy link
Contributor

pdf commented Jan 26, 2017

Can you tell me why https://github.com/gopherjs/vecty/blob/34ae77073fd923c6ab2f89c234a8ecf76f463cb0/dom.go#L175 exists?

Would it not be safe to just continue here?

@dmitshur
Copy link
Contributor

The commit message of ee9d6c9 says:

panic if nextChildRender == prevChildRender (very invalid state)

See #70

@pdf
Copy link
Contributor Author

pdf commented Jan 27, 2017

Sorry, should have used blame to start, however I've read the issue and the related code, and I still may be confused.

The upshot seems to be that for every render, nested components must be reinstantiated. I think I can see why that is as the code stands, but it makes natural patterns produce this panic, which is confusing.

The obvious pattern is mentioned in #70 - passing and storing components to be nested. This can be worked around as Dave did by passing a closure that returns new instances of the components, but this doesn't seem ideal.

I think we can support at least Components by just calling Rerender if prevChild == nextChild, ie:

diff --git a/dom.go b/dom.go
index 7ee1c9c..12fa374 100644
--- a/dom.go
+++ b/dom.go
@@ -158,8 +158,8 @@ func (h *HTML) restoreHTML(prev *HTML) {

        // TODO better list element reuse
        for i, nextChild := range h.children {
-               nextChildRender := doRender(nextChild)
                if i >= len(prev.children) {
+                       nextChildRender := doRender(nextChild)
                        if doRestore(nil, nextChild, nil, nextChildRender) {
                                continue
                        }
@@ -167,6 +167,11 @@ func (h *HTML) restoreHTML(prev *HTML) {
                        continue
                }
                prevChild := prev.children[i]
+               if c, isComponent := nextChild.(Component); isComponent && prevChild == nextChild {
+                       Rerender(c)
+                       continue
+               }
+               nextChildRender := doRender(nextChild)
                prevChildRender, ok := prevChild.(*HTML)
                if !ok {
                        prevChildRender = prevChild.(Component).Context().prevRender

Not certain on how to handle HTML. Thoughts?

@slimsag
Copy link
Member

slimsag commented Jan 28, 2017

Removing this panic is NOT a valid thing to do. While you may find a way to make this work technically in the code -- it's not a good thing to be doing in general: a component Render should always propagate downwards into child components, rendering the entire tree of elements from scratch.

The motivation for this goes back to the very core of what React & Vecty do: you give us a new render of the DOM, and we handle things behind the scenes. Storing a previous render and returning it subsequently in a secondary render is a lot like manipulating the browser DOM natively -- it's just not a good practice.

I understand the desire to use a pattern like that mentioned in #70 (comment) but we'll need to find a way to do it nicely without compromising on the core idea that Render really does return a new virtual DOM tree.

@slimsag slimsag changed the title Panic on nextChildRender == prevChildRender idiomatic way to pass child component lists into an element's constructor Jan 28, 2017
@pdf
Copy link
Contributor Author

pdf commented Jan 28, 2017

I believe the diff above does what you're describing, but works only for Components, not HTML. It leaves the panic in place.

@pdf
Copy link
Contributor Author

pdf commented Feb 3, 2017

Would you be willing to accept a PR for the above patch in the mean time? It solves enough of the issue to work for me (I only need to retain persistent vecty.Components, not *vecty.HTML).

pdf added a commit to pdf/vecty that referenced this issue Mar 14, 2017
Partial fix for hexops#78, only allows re-use of *vecty.Component, not
*vecty.HTML
@gernest
Copy link
Contributor

gernest commented Apr 27, 2017

gentle ping @pdf @slimsag @shurcooL I came across this , I'm trying to port material design lite to vecty. I'm still puzzled what does render variable mean.

If you don't mind can you enlighten me abit here.

Here is my component, it is a tab panel

type Panel struct {
	vecty.Core
	IsActive bool
	Name     string
	ID       string
	Children vecty.MarkupOrComponentOrHTML
}

func (p *Panel) Render() *vecty.HTML {
	c := make(vecty.ClassMap)
	c["mdl-tabs__panel "] = true
	if p.IsActive {
		c["is-active"] = true
	}
	return elem.Div(
		c,
		p.Children,
		prop.ID(p.ID),
	)
}

Rerendering this component results into the panic. And I need to rerender this to reflect tab selection, I have tried several setps that seemed to not work. Now I have a feeling I missed something and that my understanding on how vecty work is limited.

The Children field is important as user defined content of the tab.

Any ideas will be highly appreciated

Thanks

@pdf
Copy link
Contributor Author

pdf commented Apr 27, 2017

@gernest the render variable is internal to vecty. You are right to suspect the Children field to be responsible for your woes - you could try pulling in my patch from #94 to see if it resolves your problem, and you can watch #94 for merge status, however @slimsag said that he would like to add some tests first, so it might be a while.

PS - MDL is deprecated in favour of material-components-web.

@gernest
Copy link
Contributor

gernest commented Apr 27, 2017

@pdf thanks. I think I will try to avoid re rendering the component altogether, I will do direct dom manipulation for the same effect.

PS - MDL is deprecated in favour of material-components-web.

I heard about that, but I have to start somewhere. If having a full mdl port is possible, then migrating it to material-components-web. should be possible as well.

pdf added a commit to pdf/vecty that referenced this issue May 7, 2017
Partial fix for hexops#78, only allows re-use of *vecty.Component, not
*vecty.HTML
pdf added a commit to pdf/vecty that referenced this issue May 7, 2017
Partial fix for hexops#78, only allows re-use of *vecty.Component, not
*vecty.HTML
@pdf pdf mentioned this issue May 10, 2017
@slimsag
Copy link
Member

slimsag commented Jul 1, 2017

This is most likely fixed, as @pdf said in #124:

I think #78 should be close to solved with this merge, combined with the rework of render/renderComponent/reconcile, but I've not tested sufficiently to call it conclusively.

I think this is correct, but let's keep this issue around for adding proper testing of this functionality. I also wonder how this will influence or need to be changed based on whatever we do for #115

@slimsag slimsag added this to the 1.0.0 milestone Jul 4, 2017
@slimsag slimsag changed the title idiomatic way to pass child component lists into an element's constructor Add testing for 'idiomatic way to pass child component lists into an element's constructor' Oct 14, 2017
@slimsag
Copy link
Member

slimsag commented Oct 14, 2017

Updated title to reflect what needs to be done here, per my last message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants