astroturf icon indicating copy to clipboard operation
astroturf copied to clipboard

Use hooks to avoid big React tree by HOC

Open ai opened this issue 5 years ago • 6 comments

There is a complains that Astroturf uses too many HOCs which create big React tree.

https://twitter.com/oleg008/status/1111215885275017218

Hooks could improve the case

ai avatar Mar 28 '19 15:03 ai

It's not really a complaint about HOCs, it's more about "zero runtime" not actually being zero, because of the HOC overhead and this is not clearly expressed in the docs.

Styled Components pattern requires you to wrap every native component with a HOC, this is how it generally works. With hooks you are gonna need a completely different interface.

kof avatar Mar 28 '19 15:03 kof

yeah there isn't really a good way to use hooks here i don't think. I also don't quite understand how to make it clearer that it's happening...if you want a component that's styled you really can't avoid creating a component. Which is to say that maybe if you want want components the css tag is the best option, since it only produces classes

jquense avatar Mar 28 '19 16:03 jquense

OK, let’s create issue

ai avatar Mar 28 '19 16:03 ai

Well, it's zero-runtime if you use the CSS tag.

I feel like a nice API is possible, though. Something like:

function MyComponent(props) {
  const className = useStyled`
    color: black;

    &.primary {
      color: blue;
    }
  `(props);

  return <a className={className} />;
}

This specific example is a little dumb, but it'd allow implementing a little more logic.

taion avatar Mar 28 '19 16:03 taion

I wonder if we could be really clever here and just support:

<a className={css`...`} />

taion avatar Apr 05 '19 15:04 taion

Could also use spreads:

const container = styled`
  background: white;
  &.condition {
    background: red;
  }
`;

<div {...container}/>
<div {...container({condition: true})}/>
<div className={`style ${container}`}/>

styled`...` returning something looking roughly like this

function container(props) {
  return { className: 'container' + (props.condition ? ' container-condition' : '') };
}
container.className = 'container';
container.toString = () => 'container';

This maintains the very useful conditional classes functionality and also makes it easy to combine class names (the toString). I'm not 100% sure if the above approach will work nicely with classnames however.

It also enables the often requested property: {props => props.x} to some extent, by allowing style to spread in (this is a bit hacky and won't play nice with multiple class names or a custom style unless you do things manually). The composition concerns can be re-mediated to some extent by also exporting a helper that merges your styles (this helper can also implement some memoization to prevent unnecessary rerenders).

It's a pretty big departure from other patterns but I think it offers a lot of benefits while remaining concise.

HRDepartment avatar Apr 05 '19 19:04 HRDepartment