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

Register children as props #48

Open
timkelty opened this issue Dec 9, 2019 · 4 comments
Open

Register children as props #48

timkelty opened this issue Dec 9, 2019 · 4 comments

Comments

@timkelty
Copy link

timkelty commented Dec 9, 2019

I thought it would be neat if we were able to register children of the component root:

<div data-component="SomeReactComponent">
    <div data-prop="foo"><p>Some markup rendered from the CMS that I'd rather not pass as data</p></div>
</div>

This could also be used as progressive enhancement, or a way inject SEO data.

@timkelty
Copy link
Author

FWIW, I tried to make a custom factory for doing this, but by the time the factory's inject method is called, target is already the modified data-habitat element, and thus the children have been removed.

@timkelty
Copy link
Author

timkelty commented Dec 18, 2019

Here is my custom factory that does this…it is a bit hacky because it relies on the replaceDisabled option being true.

class MyCustomFactory {
  inject(module, props, target) {
    if (target) {
      const childProps = this.getChildProps(target);

      ReactDOM.render(
        React.createElement(module, Object.assign({}, childProps, props || {})),
        target,
      );

    } else {
      Logger.warn('RHW07', 'Target element is null or undefined.');
    }
  }
  dispose(target) {
    if (target) {
      ReactDOM.unmountComponentAtNode(target);
    }
  }

  getChildProps(target, remove = true) {
    const {previousElementSibling} = target;
    const children = previousElementSibling && previousElementSibling.querySelectorAll('[data-prop]') || [];

    return [...children].reduce((obj, el) => {
      if (remove) {
        el.remove();
      }

      return Object.assign(obj, {
        [el.dataset.prop]: el,
      });
    }, {});
  }
}

Example Usage…

<div data-component="MyComponent" data-props='{"foo": "bar"}' data-habitat-no-replace="true">
  <div data-prop="someContent">
    <p>Some content…</p>
  </div>
</div>
<div dangerouslySetInnerHTML={{__html: this.props.someContent.innerHTML}} />

@iainsimmons
Copy link

Hey @timkelty, I just found a built-in way to do this, using the proxy prop that is passed to all react-habitat components:

<div dangerouslySetInnerHTML={{__html: this.props.proxy.innerHTML}} />

That wouldn't do any of the child props stuff, but you could do that with something like htmr (which is what I am using) to convert the HTML string to React components.

Then you wouldn't need the data-prop syntax:

import React from 'react';
import convert from 'htmr';

const MyComponent = ({foo, proxy}) => (
  <div>
    <p>{foo}</p>
    {convert(proxy.innerHTML)}
  </div>
);
<div data-component="MyComponent" data-props='{"foo": "bar"}'>
  <div className="my-class">
    <p>Some content…</p>
  </div>
</div>

Renders something like this:

<div data-habitat="C1">
  <div>
    <p>bar</p>
    <div class="my-class">
      <p>Some content…</p>
    </div>
  </div>
</div>

@timkelty
Copy link
Author

@iainsimmons oh nice!

Every React Habitat instance is passed in a prop named proxy, this is a reference the original dom element.

Didn't realize that!

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

2 participants