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

Question: mysterious vnode.children nesting #2106

Closed
barneycarroll opened this issue Mar 15, 2018 · 5 comments
Closed

Question: mysterious vnode.children nesting #2106

barneycarroll opened this issue Mar 15, 2018 · 5 comments

Comments

@barneycarroll
Copy link
Member

barneycarroll commented Mar 15, 2018

I'm playing around with feasibility / ergonomics studies for functor components. Precedent React convention tends towards components that expose a custom attribute / props interface and accept the view / render function in the place of children, which makes a certain amount of sense and certainly involves less typing ceremony.

When I try to implement this pattern in Mithril I end up unexpectedly having to query vnode.children[0].children from the consuming component to access the function I passed in as the last argument — I would expect this to be exposed as vnode.children[0].

Why might this be? Does this make sense to people more familiar with the internal vnode engine? Have I hit upon a dangerous ambiguity, or is this a harmless piece of implementation trivia?

Here's a minimal repro.

@barneycarroll barneycarroll changed the title Question: mysterious vnode children Question: mysterious vnode.children nesting Mar 15, 2018
@tivac
Copy link
Contributor

tivac commented Mar 15, 2018

Ultimately it's because a bare function isn't a valid child node, you're running into some normalization code that does its best with bad input.

¯\_(ಠ_ಠ)_/¯

Stepping through it does this.

A function isn't a valid attribute, so it's a child node. All children get normalized, so it goes through this function:

Vnode.normalizeChildren = function normalizeChildren(children) {
for (var i = 0; i < children.length; i++) {
children[i] = Vnode.normalize(children[i])
}
return children
}

Which calls this function:

Vnode.normalize = function(node) {
if (Array.isArray(node)) return Vnode("[", undefined, undefined, Vnode.normalizeChildren(node), undefined, undefined)
if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined)
return node
}

Which specifically is hitting this conditional:

if (node != null && typeof node !== "object") return Vnode("#", undefined, undefined, node === false ? "" : node, undefined, undefined)

Which then runs this:

function Vnode(tag, key, attrs, children, text, dom) {
return {tag: tag, key: key, attrs: attrs, children: children, text: text, dom: dom, domSize: undefined, state: undefined, events: undefined, instance: undefined, skip: false}
}

With the children property set to your function.

So ultimately the shape of what your flems returns looks like this:

{
  "tag": {
    "view": v => v.children[0].children()
  },
  "attrs": {},
  "children": [
    {
      "children": () => 'Hello!'
    }
  ]
}

and tada to execute your function you need to use vnode.children[0].children(). Does that help?

@barneycarroll
Copy link
Member Author

Brilliant, thanks @tivac. I assume the generous fall-through to text node condition is there to enable things along the lines of streams' .toString()? …in which case I assume the implementation I'm looking at for m(Component, attrs?, function) is relatively safe for the time being?

@pygy I know you've been working around the space of more formal normalisation procedures in the land of vtree disambiguation, is this particular mechanism under scrutiny?

@pygy
Copy link
Member

pygy commented Mar 15, 2018

Isiah (not pinging him here since he's on a Mithril hiatus) suggested supporting a function as the only child for components in #2050, and I shot it down ignoring the broader context, suggesting to use an attr instead... We may revisit it if the pattern is that useful.

@barneycarroll
Copy link
Member Author

I can't believe I missed that issue! Now I understand the underlying logic I don't think the children[0].children dance is that onerous: Function-as-child as a first class API scenario has no value in the absence of an involved component written specifically to invoke it, in which case children[0] is only slightly less odd to write and read with regards to what's being done. Thanks for the pointers everyone.

@pygy
Copy link
Member

pygy commented Mar 17, 2018

Still. that's not very clean, and it works by accident. That's undocumented, untested behaviour that may break without notice... Let's resurrect #2050.

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

No branches or pull requests

3 participants