Higher-order components in Hyperstack

104 Views Asked by At

There's a frequent usecase with javascript libraries where you want to decorate your components with higher order components.

For example, the material-ui library includes a styling higher-order component withStyles.

In javascript, you would do

import { withStyles } from '@material-ui/core';

const styles = {
  example: {
    padding: 8
  }
}
const SomeComponent = ({classes}) => <div className={classes.example}>I'm a component</div>;

export default withStyles(SomeComponent);

How can you achieve the same in Hyperstack?

1

There are 1 best solutions below

1
On BEST ANSWER

First off looks like there is an issue that you have to patch. This will be fixed in the next point release: Just add this method to your Hypercomponent base class (app/hyperstack/components/hypercomponent.rb)

def self.to_n
  Hyperstack::Internal::Component::ReactWrapper.create_native_react_class(self)
end

Now if you have the following styles:

MY_STYLES = {root: {backgroundColor: 'red'}}

and a component that you want to style:

class StyleTest < HyperComponent
  param :some_param
  param :classes
  render do
    DIV(class: classes[:root]) { some_param }
  end
end

You can do so like this:

class StyledTest1 < HyperComponent
  imports `Mui.withStyles(#{MY_STYLES.to_n})(#{StyleTest.to_n})`
end

What is happening is we are dropping out to JS using the backticks and calling Mui.with_styles directly and passing it MY_STYLES (just like in the MUI doc example). The to_n converts from a Ruby Hash to JS object. (When passing params to components this is automatic, but not so with simple function calls.)

Then we call the resulting HOC with our StyleTest class (also calling to_n to convert from a Ruby class to a simple JS class.)

Finally, we import it back into a Hyperstack component class.

That is a little more work than I like so we can just add a handy helper method to our HyperComponent class:

class HyperComponent
  include Hyperstack::Component
  include Hyperstack::State::Observable
  param_accessor_style :accessors  # this is now the prefered param style

  # patch for issue: https://github.com/hyperstack-org/hyperstack/issues/153
  def self.to_n
    Hyperstack::Internal::Component::ReactWrapper.create_native_react_class(self)
  end

  # our helper macro:
  def self.add_styles(style, opts = {})
    imports `Mui.withStyles(#{style.to_n}, #{opts.to_n})(#{superclass.to_n})`
  end
end

Now we can add styles like this:

class StyledTest2 < StyleTest
  add_styles MY_STYLE
end

and now we have a new Component Class with our style in it.

For example:

class App < HyperComponent

  render do
    DIV do
      StyledTest1(some_param: 'test 1')
      StyledTest2(some_param: 'test 2')
    end
  end
end