Inferno: Ref on custom component

Created on 8 Feb 2017  路  17Comments  路  Source: infernojs/inferno

ref callbacks seem to work work fine when placed directly on an html element. However it doesn't seem to trigger if placed on a component that I've created. Any ideas?

question

Most helpful comment

@wagerfield He meant this http://codepen.io/TrySound/pen/mRzMer

All 17 comments

Update it looks like it's only an issue with functional components

This definitely sounds like bug @dax0 do you have simple steps to reproduce?

http://codepen.io/TrySound/pen/QdVXGp
Functional doesn't work at all. Class passes class instance - is it a normal case?

Yeah it should pass class instance.

What should pass functional?

Functional components don't support refs as they have no state or instance to bind to

Okay this is not a bug, maybe feature request.

What would you use ref on a functional component for? We have a onComponentDidMount lifecycle hook which (probably) can be used for what you want.

By the way is it a good or bad practice to use onComponentDidMount as ref for components?

Yeah I realized I would eventually need to convert this to a stateful component, was just confusing because it seemed like refs weren't working at all.

I can't really think of a valid reason to have a ref on a functional component itself. However does passing down the ref to a child element make sense? I tend to wrap most elements in a functional component (think input textarea etc) so I can apply styling and I pass down all the props. It would be nice to pass down the ref callback to them.

I think it would be useful, perhaps even necessary to pass ref down on props when creating higher order _functional_ components as @dax0 suggested. Why is it stripped from props in the first place?

I am in the process of authoring the styletron-inferno bindings and would like to be able to use a functional component over a class component, but am currently unable to do so because of this limitation of functional components.

Styletron's styled function for React (I am porting for Inferno) takes 2 arguments: name and styles. name can be a string ('div', 'h1' etc.) or a class/functional component and styles can be an object with CSS property/value pairs or a function that gets called with props and context鈥攔eturning an object with CSS property value pairs.

The idea is that you take an existing class/functional component (or create a new DOM element) and then return a new higher order component with the className/styles applied鈥攅ssentially decoupling a component from it's styles.

Below is a simplified implementation of the styled function:

function styled(name, styles) {
  return function(props, context) {
    // Returns the className string like "a b c d"
    const className = StyletronUtils.injectStylePrefixed(context.styletron, styles)
    props.className = `${props.className || ''} ${className}`
    // props.ref doesn't exist, so there is no way 
    // of getting a reference to the inner element
    return createElement(name, props)
  }
}

Notice that in the above I am returning a functional component as opposed to creating a class component and returning that.

However, when you come to use the styled function with the above implementation (returning a functional component), it prevents you from getting a ref to the inner component or DOM element:

const StyledInput = styled('input', (props) => ({
  backgroundColor: props.valid ? 'green' : 'red'
}))

class SomeComponent extends InfernoComponent {
  componentDidMount() {
    console.log(this.input) // undefined
  }
  render() {
    const { valid } = this.state
    return (
      <StyledInput 
        valid={valid}
        ref={el => this.input = el} // Never gets called
        onChange={linkEvent(this, this.handleChange)}
      />
    )
  }
  handleClick() {
    this.setState({ valid: !!this.input.value }) // Error: Cannot read property value of undefined
  }
}

Inferno.render(<SomeComponent/>, document.getElementById('root'))

With the above implementation, the user would have to use Inferno's onComponentDidMount lifecycle hook on the StyledInput as opposed to ref. This is no bad thing, I guess...but it feels a little funky鈥攚hat are your thoughts @trueadm?

To quickly just state something, ref and key are special cases that can never be props. If you want to pass something down, use another name for the property other than those names. This is because they are stripped and applied to the root of the VNode itself and this is how React and other libraries work too. For example pass down elementRef which tells the component to use ref on the DOM node and pass back the context to that of the function passed via elementRef.

@trueadm, I searched the repo for elementRef and couldn't find anything?

http://codepen.io/wagerfield/pen/zNmzza

@wagerfield He meant this http://codepen.io/TrySound/pen/mRzMer

Looking at styled-components (a lib that Styletron was influenced by) they approached this problem with the same method described above鈥攂ut used a special prop called innerRef.

Looking at the implementation of styletron-react, @rtsao has taken the same approach, so I will do the same for styletron-inferno.

See here and here.

Yeah I guess we could use a different name. Just would have been nice to pass down props blindly.

There is currently no plans changing this behavior. Closing

Was this page helpful?
0 / 5 - 0 ratings

Related issues

aprilmintacpineda picture aprilmintacpineda  路  22Comments

vinej picture vinej  路  24Comments

simonjoom picture simonjoom  路  21Comments

tejacques picture tejacques  路  22Comments

trueadm picture trueadm  路  32Comments