robinweser / fela

State-Driven Styling in JavaScript
http://fela.js.org
MIT License
2.27k stars 184 forks source link

Bring innerRef prop to createComponent API #190

Closed javivelasco closed 7 years ago

javivelasco commented 7 years ago

Version: 3.0.x Environment: DOM Type: Feature

Proposal

Hi there! I was trying out Fela and it's a great work with a very interesting approach, congrats! I think there is something missing with the React bindings that could be interesting for you to implement. I'm working on a cross-style implementation of React components and there are situations where you need a ref to the element. Some components like input have an imperative API that may need to be called under specific circumstances.

I noticed the createComponent implementation provide a similar API to styled-components but it generates always stateless components. This makes impossible to get a reference to a component without directly querying the DOM.

I proposed to implement a similar API to both styled-components and styletron where you can pass an innerRef that gets assigned to the corresponding component making possible to get a reference to the real component from upside.

Code Example:

import React, { Component } from 'react';
import { createComponent } from 'react-fela';

const StyledComponent = createComponent(props => ({
  backgroundColor: 'red',
}), 'span');

class Agnostic extends Component {
  handleClick = event => {
    console.log('Here is the ref:', this.rootNode);
  };

  render() {
    const self = this;
    return (
      <StyledComponent 
        {...this.props}
        onClick={this.handleClick} 
        innerRef={(node) => self.rootNode = node; } 
      />
    );
  }
}

Does it makes sense?

robinweser commented 7 years ago

This is pretty valid. I was using the connect API shipped by react-fela for stateful components, but I don't like the API at all (which is why it is not actively advertised as well). The approach with the innerRef seems to solve this in a simple and yet powerful way.

Although, you could also do the following (as createComponent also allows custom components):

import React, { Component } from 'react';
import { createComponent } from 'react-fela';

const StyledComponent = createComponent(props => ({
  backgroundColor: 'red',
}), ({ innerRef,...otherProps }) => (
  <span {...otherProps} ref={innerRef} />
));

class Agnostic extends Component {
  handleClick = event => {
    console.log('Here is the ref:', this.rootNode);
  };

  render() {
    const self = this;
    return (
      <StyledComponent 
        {...this.props}
        onClick={this.handleClick} 
        innerRef={(node) => self.rootNode = node; } 
      />
    );
  }
}
ctrlplusb commented 7 years ago

Thanks for the "workaroud" @rofrischmann.

Minor fix though, you need to specify the pass through props too:

const StyledComponent = createComponent(
  props => ({ backgroundColor: 'red' }), 
  ({ innerRef,...otherProps }) => <span {...otherProps} ref={innerRef} />,
  ['innerRef'] // 👈
);
javivelasco commented 7 years ago

@rofrischmann By the way, that is exactly the workaround I was using :)

javivelasco commented 7 years ago

:shipit: Cool!