Hyperapp: {...props} causes TypeError

Created on 15 Feb 2017  ·  22Comments  ·  Source: jorgebucaran/hyperapp

This code crashes due to children being assigned to the <div>. I think it works in React. I'm assuming React doesn't pass children when it's empty. Not sure though.

TypeError: Attempted to assign to readonly property.

TypeError: undefined is not an object (evaluating 'e.childNodes')


Broken Code

export default ({...props}) =>
    <div {...props}>Example!</div>

Working Code

export default ({children, ...props}) =>
    <div {...props}>Example!</div>
Bug Discussion

Most helpful comment

In Inferno & Preact, children gets stripped from the props object.

All 22 comments

@selfup Are you familiar with React or what they do / how it works in this situation?

In Inferno & Preact, children gets stripped from the props object.

I believe it would be better to always defaults it to empty array, so no errors would happen.

@tunnckoCore shouldn't that actually cause the issue? 🙂

Glad you all figured it out!

This is an odd place for childNodes however, in React you would do something like this injectProps function here: https://github.com/selfup/react-store-lab-example/blob/master/src/ReactStoreLab.js#L17

So, in Inferno and Preact, children is removed from props? What about React?

With React it's still in there if there are children as the example above ^ ^

React has an amazingChildren API.

You can clone a single element, or use the custom React.Chilren.map to inject props into all children


Explicit destructuring is advised but I can see how using the spread operator here can be beneficial when there are many props

React.Children API

https://facebook.github.io/react/docs/react-api.html#react.children

@dodekeract

Component functions only receive a single data (props) argument, why then:

export default ({children, ...props}) =>
    <div {...props}>Example!</div>

I think I'll need an example I can actually run in order to figure this out.

@dodekeract don't think so

@jbucaran same here, i can't get what can be the reason too. With or without children, it not make sense to fail.

@jbucaran I just re-read your comment from 2 days ago.

I think you're not that familiar with the object spread syntax, let me explain.

{children, ...props} does the following:

  1. extract children from the properties (basically discard it)
  2. extract all other properties into props

It's still only one argument, hence the { }.

The bug was caused because children was passed into the <div>, which apparently is (or was) illegal in hyperapp.

@dodekeract Can you confirm that the bug you were having was fixed?

@jbucaran I will later today.

@dodekeract Please do 🙏 and thanks.

I can still reproduce this in 0.4.1.

Working
Broken

@jbucaran Please re-open.

@dodekeract Okay, two things we can do:

  1. Strip children from props _a-la_ Preat/Inferno (break the API with it) and go with (props, children).
  2. Skip children magically when setting element data in createElementFrom(node).

Both are simple to do, simplest is 1, but 2 is frictionless and Just Works™.

I'm more inclined towards 1, since perfect React compatibility is not a goal in this project.

I'd go with option 1. Especially since you're not shipping hyperx anymore, and instead welcoming any plugin-renderers, they nearly all expect children to be its own parameter.

Then again, I'm not familiar enough with hyperapp and the implications this will entail.

React compat should not be included. It should be easy to do hyperapp-compat or something like that, some wrappers and etc. Like inferno has inferno-compat. So yea, we should not care about react. We are not react and we can't do much things that react can do (maybe? i'm almost sure).

As about (props, children) it is totally correct btw. Because transpiled JSX never sets children to props. I faced that yesterday playing with mich-h and jsx.

So this is perfectly fine

  if (typeof selector === 'function') {
    return selector(node.properties, node.children)
  }

And in called component you can get children from props, so ({ foo, bar, name }, children) is the right thing.

Another interesting thing when passing children to props was that you can use such thing

var Foo = ({ name, children }) => <div>
  <h1>{ name }</h1>
  <p>{ children }</p>
</div>

var link = <a href="#">Hello World</a>
var tree = <Foo name="Charlike" children={ link } />

instead of the link inside the Foo tag

var link = <a href="#">Hello World</a>
var tree = <Foo name="Charlike">{ link }</Foo>

Kinda like the first one and found it useful.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zaceno picture zaceno  ·  3Comments

jorgebucaran picture jorgebucaran  ·  3Comments

jamen picture jamen  ·  4Comments

ghost picture ghost  ·  3Comments

Mytrill picture Mytrill  ·  4Comments