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

prop that accepts array or object returns string #134

Closed
psyrendust opened this issue Sep 6, 2017 · 8 comments
Closed

prop that accepts array or object returns string #134

psyrendust opened this issue Sep 6, 2017 · 8 comments
Assignees

Comments

@psyrendust
Copy link

psyrendust commented Sep 6, 2017

Issue

If I have a stencil custom element that has a prop that is typed as an array of numbers number[], the value of the prop from within the custom element is a string.

Expected

I expect the prop would be an array of numbers.

HTML Example

import {Component, Prop} from '@stencil/core';

@Component({
  tag: 'my-map',
})
export class MyMap {
  @Prop() name: string;
  @Prop() coords: number[] = [-122.40655, 37.78437];

  componentDidLoad() {
    console.log(this.name, typeof this.coords, this.coords); // => object (2) [-122.40655, 37.78437]
                                                             // or
                                                             // => string -122.40655,37.78437
  }
}
<!DOCTYPE html>
<html dir="ltr" lang="en">
<head>
  <meta charset="utf-8" />
  <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
  <title>Stencil Starter App</title>
  <script src="build/app.js"></script>
</head>
<body>

  <my-map name='no prop'></my-map> <!-- => "no prop array" //-->
  <my-map name='with prop' coords='-122.4,37.7'></my-map> <!-- => "with prop string" //-->

</body>
</html>

I'm noticing this because I'm integrating stencil custom elements into a react application. When I pass in an array to a prop it get's converted by react to a string. So when I'm trying to read the value of the prop it's a string instead of an array of numbers.

The same holds true when trying to pass in an object to a prop.

React Example

import {Component, Prop} from '@stencil/core';

@Component({
  tag: 'my-map',
})
export class MyMap {
  @Prop() coords: number[] = [-122.40655, 37.78437];
  @Prop() foo: any;

  componentDidLoad() {
    console.log(typeof this.coords, this.coords); // => string -122.40655,37.78437
    console.log(typeof this.foo, this.foo); // => string [object Object]
  }
}
import React, { Component } from 'react';

export default class MyView extends Component {
  render() {
    const coords = [-122.40655, 37.78437];
    const foo = {
      bar: 'woot',
    };
    return (
      <my-map coords={coords} foo={foo}></my-map>
    );
  }
}
@kylecordes
Copy link

@psyrendust I think this is a React issue. React in its current version passing in attributes (strings) to custom elements, Whereas many of them (like yours and mine...) need JavaScript data via properties. You can read a lot about it in this open issue:

facebook/react#7249

I believe the typical workaround is to pass this form of data using a bit of JavaScript in the current version of React.

@psyrendust
Copy link
Author

@kylecordes So it sounds like I'd have to do something like this:

import React, { Component } from 'react';

export default class MyView extends Component {
  render() {
    const coords = [-122.40655, 37.78437];
    const foo = {
      bar: 'woot',
    };

    const handleRef = (myMap) => {
      myMap.coords = coords;
      myMap.foo = foo;
    };

    return (
      <my-map ref={handleRef}></my-map>
    );
  }
}

If so does this mean that my CE would also have to implement the upgradeProperty hack? https://jsbin.com/zicewal/6/edit?html,output

@kylecordes
Copy link

kylecordes commented Sep 6, 2017

@psyrendust I don't use React as often as some of its competitors :-) so I can't tell you the exact most concise invocation. But if you watch this Rob Dodson presentation starting at the time linked below, you should get a full feel for the situation.

https://youtu.be/sK1ODp0nDbM?t=24m40s

He launched a great informational site:

https://custom-elements-everywhere.com/

... Which documents the numerous bits a framework must do for maximal custom element / web component compatibility, and then tests a bunch of the current contenders. React currently is in the unfortunate position of being quite popular yet scoring pretty poorly. I expect that will be addressed in an upcoming React version to get it back on the top of the heap with the other major contenders which already support custom elements very well.

All of this is equally relevant to Stencil or any other way of building web components.

@jthoms1
Copy link
Contributor

jthoms1 commented Sep 8, 2017

@psyrendust I have ran into this issue with React and created a simple utility function to work around the problem. I have talked with the React team and this is something they are talking about fixing in React 17. Until then we have a workaround.

This utility should be used for custom events or for objects/arrays. Strings, numbers and booleans should work fine.

This utility fixes the issue.
https://github.com/ionic-team/ionic-react-conference-app/blob/master/src/utils/stencil.js

Usage is below:

import React, { Component } from 'react';
import { wc } from '../utils/stencil';

export default class MyView extends Component {
  render() {
    const coords = [-122.40655, 37.78437];
    const foo = {
      bar: 'woot',
    };
    return (
      <my-map
        ref={wc({
          coords: coords
        })}
        foo={foo}></my-map>
    );
  }
}

@jthoms1 jthoms1 closed this as completed Sep 8, 2017
@psyrendust
Copy link
Author

@jthoms1 Thanks for the util! Considering that foo is also an object I'm assuming it would look like this (using object shorthand):

import React, { Component } from 'react';
import { wc } from '../utils/stencil';

export default class MyView extends Component {
  render() {
    const coords = [-122.40655, 37.78437];
    const foo = {
      bar: 'woot',
    };
    return (
      <my-map
        ref={wc({ coords })}
        foo={wc({ foo })} />
    );
  }
}

@jthoms1
Copy link
Contributor

jthoms1 commented Sep 11, 2017

wc is a function specific to ref so all values would be passed through that.

import React, { Component } from 'react';
import { wc } from '../utils/stencil';

export default class MyView extends Component {
  render() {
    const coords = [-122.40655, 37.78437];
    const foo = {
      bar: 'woot',
    };
    return (
      <my-map
        ref={wc({ coords, foo })} />
    );
  }
}

@marisaoliva
Copy link

please, Can you put the utility again? :D

I've problem with the link and I've the same issue

https://github.com/ionic-team/ionic-react-conference-app/blob/master/src/utils/stencil.js

@marisaoliva
Copy link

Hello

I've found this
https://www.npmjs.com/package/reactify-wc
;)

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

5 participants