DeloitteAU / react-habitat

⚛️ 🛅 A React DOM Bootstrapper designed to harmonise a hybrid 'CMS + React' application.
Other
262 stars 43 forks source link

Habitat incompatible with `{children}`? #29

Closed ryanpcmcquen closed 6 years ago

ryanpcmcquen commented 6 years ago

I have a button that takes {children} as text content (rather than having a separate text prop). Is this possible in Habitat, or does everything that is rendered need to be a prop?

Source for the button is here: https://github.com/ryanpcmcquen/react-habitat-poc/blob/master/source/components/Button/Button.js

jennasalau commented 6 years ago

Hey Ryan,

Children is an interesting one. It gets tricky because React Habitat essentially only provides a React DOM interface from HTML. Now React children expects type React Component/Node which we don't have in HTML. Only DOM Elements. In fact, the inner HTML is getting used as fallbacks by others.

Fair enough you are only trying to pass in a text node, but it gets much more complicated if it was another React component. For this reason I'm reluctant to add direct support for children as its a very "React" thing and not so much a "React DOM / Habitat" thing. I would rather this library stay dumb to React itself.

In saying that though, you have a few options:

Option 1 children is available on props anyway so there is no reason you can't do the following. The only caveat is obviously it needs to be a text node. (I haven't tested this btw).

<div data-component="Button" data-prop-children="My Button Text" />

Option 2 You change your Button component slightly to accept children OR text prop.

render() {
    return <a>{this.props.children || this.props.text}</a>;
}

Then

<div data-component="Button" data-prop-text="My Button Text" />

Option 3 You use the proxy prop to read the innerHTML from the target element and turn it into a React component.

eg in your Button (again this is very untested, just brain dumping)

constructor(props) {
    const children = React.createElement(props.proxy.innerHTML);
}

You might have better millage with something like https://roman01la.github.io/html-to-react-components/

I would still advise against option 3 anyway as it tightly couples your components to React Habitat. This should be a last resort.

ryanpcmcquen commented 6 years ago

@jennasalau, thank you for the thorough response. It seems that option 1 would be the best, since it keeps the components from having extra logic, merely to work inside Habitat. As a random question, how would you approach building a slideshow component in React Habitat? For example, one that could take different types of cards (photo, video, product) ...

jennasalau commented 6 years ago

No problem :)

I would get the backend to pass the slide show "data" through in JSON format

For example

{
    "slides": [
          {
               "media": "/banner.png",
               "title": "Cool Banner",
               "description": "A nice description",
               "type": "IMAGE",
               "href": "/some-link",
               "meta": {}
          },
          {
               "media": "http://youtube.com/videoid",
               "title": "Cool Video",
               "description": "A nice description",
               "type": "VIDEO",
               "href": "/some-link",
               "meta": {}
          },
          {
               "media": "/product-image.png",
               "title": "Check out this product",
               "description": "A nice description",
               "type": "PRODUCT",
               "href": "/some-link",
               "meta": {
                     "price": 29.99
               }
          },
    ]
}

I would then have a specific component that converts that JSON into a React slideshow. This one i would expose to React Habitat also.

import SomeSlideShowLibarary from 'some-slide-show-library';
import Card from './Card';
import Video from './Video';
import Product from './Product';

class SlideShow extends React.Component {
     renderItems() {
         // data being the json parsed data passed in by React Habitat or a React Parent
         return this.props.data.map((slide) => {
             switch (slide.type) {
                  case 'IMAGE':
                       return <Card text={slide.title} img={slide.media} />;
                  case 'VIDEO':
                       return <Video text={slide.title} src={slide.media} />;
                  case 'PRODUCT':
                       return <Product text={slide.title} price={slide.meta.price} />;
                  default:
                      console.error('I don't know that type', slide.type);
                      return null;
             }
         });
     }      

     render() {
          return (
               <SomeSlideShowLibarary>{this.renderItems()}</SomeSlideShowLibarary>
          );
     }
}

Thats how i would approach it anyway.

ryanpcmcquen commented 6 years ago

You rock @jennasalau, thank you for sharing your knowledge!

ryanpcmcquen commented 6 years ago

For anyone else who is curious about this problem, we found a way to handle child components using React Habitat in slt-ui.

fsvaren commented 1 year ago

Has habitat got support for this yet? I am running into some strange issues when using react-slider, and I suspect it is related because it works well in standard preact.