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

Can we reuse React components with properties as Web Components with attributes without doing any extra work? #171

Open
AndyObtiva opened this issue Jan 31, 2024 · 9 comments

Comments

@AndyObtiva
Copy link

AndyObtiva commented Jan 31, 2024

I'm trying to understand something from the README, which seems to tell me I have to do extra work to expose React components as Web Components when using properties.

In this example:

<web-greeting name="Justin"></web-greeting>

Is there a way to actually hit the ground running with this by being able to forward any attributes to React automatically?

I have about 100 React components and I really don't want to add this code:

const WebGreeting = r2wc(Greeting, {
  props: {
    name: "string",
  },

The whole purpose of computer software is automation, and if I have to do that work manually, it's a waste of time.

I think if that is not supported automatically, you should support it by forwarding any data attributes under the property namespace automatically like this:

<web-greeting data-property-name="Justin" data-property-dob="2004-01-17" data-property-id="93292"></web-greeting>

Alternatively, forward property-* attributes instead if possible given it would be shorter syntax:

<web-greeting property-name="Justin" property-dob="2004-01-17" property-id="93292"></web-greeting>

Without support for that, this library is near useless to me, but I really need a library like this to enable using React components as Web Components.

@christopherjbaker
Copy link
Contributor

An earlier prototype of the project relied on automatically forwarding data. Unfortunately, HTML is limited to strings, but passing only strings to the React components would require major refactors of the components to handle that and would be unable to handle large data at all. We need to declare the props and types ahead of time to have data transformations. We decided that listing props and allowing us to declare types was better than only using strings.

Using your example, you likely need dob to be an actual Date object and for id to be a number. If not, you'll need to refactor the component. But how will the library know to do that? There are lots of ways for dates to be represented, and not everything that looks like a date is a date. Similarly, sometimes a string is only made of numbers but needs to stay a string.

That being said, we have looked into some way of automating the props object. We used to be able to use the propTypes definition to determine them, but the switch to TypeScript has removed this option. TypeScript does not provide runtime information on the props, but we certainly could write a utility that consumed the TypeScript at build time to provide info at runtime, much like StoryBook does. We had discussed doing this, but nobody outside of us had mentioned this being a problem for them. Now that you have, I'll definitely add this to our possible future features.

@AndyObtiva
Copy link
Author

AndyObtiva commented Jan 31, 2024

Thanks for the swift response.

Perhaps we could come up with a way to specify types at consumption time as follows:

<web-greeting 
  property-string-name="Justin" 
  property-date-dob="2004-01-17" 
  property-bigint-id="93292" 
  property-boolean-adult="true" 
  property-array-details='["Bob",50000,true]' 
  property-object-employee='{"name":"John","salary":60000}'
>
</web-greeting>

I think you get the gist of what is going on in the example above. The array and object properties are special types that are passed in as JSON. As for date, time, and datetime properties, they could have standard formats that are expected by convention when passing in the data, and they'd get automatically converted to Date objects.

Having something like this would incredibly simplify integration with React components as standard HTML Web Components.

@justinbmeyer
Copy link
Member

justinbmeyer commented Feb 1, 2024

@christopherjbaker

listing props and allowing us to declare types was better than only using strings

These things aren't mutually exclusive right?

I thought the reason the proxy was removed was because it was complex for maintainers to reason about. Is there any other reason not to include it?

@AndyObtiva the 1.7.6 version included this behavior. https://github.com/bitovi/react-to-web-component/blob/v1.7.6/src/react-to-webcomponent.js#L66

@justinbmeyer
Copy link
Member

Also, I thought we had considered a syntax like name:date="2020-10-20". This would allow folks to more quickly boot-up their R2WC usage.

@justinbmeyer
Copy link
Member

@AndyObtiva

The whole purpose of computer software is automation

Are you able to extract your properties from react and automate building the property definitions?

@AndyObtiva
Copy link
Author

AndyObtiva commented Feb 4, 2024

I am very sorry for the late response. I got carried away by work during the week, and then forgot to respond till now.

Also, I thought we had considered a syntax like name:date="2020-10-20". This would allow folks to more quickly boot-up their R2WC usage.

That would work very well if available.

Are you able to extract your properties from react and automate building the property definitions?

The plan is to be able to reuse React components from outside of React by simply using HTML Web Components. We would generate all the React component properties from either the Backend or from another Frontend library. The end result is we should be able to simply render the HTML Web Components with the right attributes to get them to render the React components, and we have all the types in advance.

Not all our React components have property definitions. The goal is not to touch the React components, yet simply expose them by names (or file paths) as HTML Web Components to reuse from outside of React. We do not like to use types or property definitions much, yet we use JavaScript as it was intended as a dynamically typed programming language. So, there is no guarantee about the availability of prop types for the React components. That said, that shouldn't matter anyways if we have an approach that would enable us to specify types at consumption time as per the example you provided above. In that case, any attribute string values we would pass in HTML Web Components would get deserialized automatically on the spot into the right types according to the types we would specify in the attribute names without caring to specify the React prop types at all (which we already implicitly know anyways). That should be good enough for us.

@shunia
Copy link

shunia commented Feb 4, 2024

I believe a change to the attribute name is hard to remember, how do I know what prefix/postfix is valid and how do I know what the actual value should be provided for the specific prefix/postfix?

I was thinking is it possible to provide a wrapper function for the property value to simplify the process instead, which not just saves the extra work of define the props ahead of time, and also saves the parse job done inside the library:

// use a map to save the value, and create a unique string as key
// the map could be cleared inside the unmount function
export function wrapper(value: any) {
  const key = `__r2wc_ww_${++_internalId}`;
  window[key] = true;
  map.set(key, value);
  return key;
}

// when attribute changes, we just need to check if the provided value is in the map or not, do not need to pre-define the props
attributeChangedCallback(attribute: string, oldValue: string, value: string) {
  // if value is not wrapped, the component do not need to consume it
  if (!map.has(value)) return;

  // after this we could just use the value stored in the map directly without the transform process
}

how to use:

import { wrapper as w } from '@r2wc/react-to-web-component'  

// in html/jsx
const _complexPropsString = w(any-complex-value);
<my-custom-element complexPorps='_complexPropsString' ></my-custom-element>

// in ts/js
document.querySelector('my-custom-element').complexProps=w(any-complex-value);

@AndyObtiva
Copy link
Author

AndyObtiva commented Feb 4, 2024

I believe a change to the attribute name is hard to remember, how do I know what prefix/postfix is valid and how do I know what the actual value should be provided for the specific prefix/postfix?

This is not a problem for us, so I wouldn't take it into account as it's not part of our requirements.

I was thinking about this problem from the point of view of simply passing dynamic attributes, which are determined on a component by component basis, without knowing them in advance nor observing their changes with a callback. I believe that would be good enough for our use case.

This mentioned solution would definitely solve our problem:

Also, I thought we had considered a syntax like name:date="2020-10-20". This would allow folks to more quickly boot-up their R2WC usage.

It is a variation on this similar solution:

<web-greeting 
  property-string-name="Justin" 
  property-date-dob="2004-01-17" 
  property-bigint-id="93292" 
  property-boolean-adult="true" 
  property-array-details='["Bob",50000,true]' 
  property-object-employee='{"name":"John","salary":60000}'
>
</web-greeting>

@Deamoner
Copy link

seems like the web component reacts equaivalent should be consider a superset on top of the component you want to remember in order to it to work seemlessly.

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