2

I'm working on extracting some code from my app into an open source package. Both my app and my package depend on the same version of React, 0.14.8.

Ever since I moved the code into the package (and made some code changes) I've been unable to render a component that uses refs. I've been searching for advice for hours, and it's yielded two basic tips:

  1. Make sure react versions aren't conflicting
  2. Make sure component with refs is created in another component's render method

As far as I can tell, I've satisfied both criteria.

class RangeQuery extends React.Component {
  static propTypes = {
    header: PropTypes.object.isRequired,
    onQueryChange: PropTypes.func.isRequired
  }

  handleChange(ev) {
    const { header } = this.props

    const start = _.isEmpty(this.refs.start.value) ? null : this.refs.start.value
    const end = _.isEmpty(this.refs.end.value) ? null : this.refs.end.value

    const values = typeof(header.query.format) === 'function' ?
      [header.query.fmtValue(start), header.query.fmtValue(end)] :
      [start, end]

    this.props.onQueryChange(header.field, header.query, values)
  }

  render() {
    return (
      <div>
        <input className="form-control" type="text" ref="start" onChange={::this.handleChange} placeholder="min" />
        <input className="form-control" type="text" ref="end"   onChange={::this.handleChange} placeholder="max" />
      </div>
    )
  }
}

const components = {
  like: LikeQuery,
  eq: EqQuery,
  range: RangeQuery
}

export default class HeaderQuery extends React.Component {
  static propTypes = {
    header: PropTypes.object.isRequired,
    onQueryChange: PropTypes.func.isRequired
  }

  render() {
    const { header, onQueryChange } = this.props
    const Query = components[header.query.type] || NoQuery

    return (
      <Query header={header} query={header.query} onQueryChange={this.props.onQueryChange} />
    )
  }
}

When I attempt to load the page with this component on it, I get the error:

invariant.js:39 Uncaught Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's render method, or you have multiple copies of React loaded

It renders if I remove the refs= from the input tags, but I obviously need those.

I have considered the alternative of writing a reducer to control the state of these components, but that's a heavy handed work around for something that should be working.

How can I fix my refs so that they'll work correctly?

Update

While I have accepted an answer on this question due to the fact that it allowed me to use refs like I wanted, the question still remains as to why it happened in the first place, and survey says the biggest suspect is multiple versions of react. As far as I can tell though, that doesn't seem to be the case.

sam@sam-macbook-ubuntu:~/myProject/client$ npm ls | grep react
├─┬ babel-preset-react@6.5.0
│ ├─┬ babel-plugin-transform-react-display-name@6.5.0
│ ├─┬ babel-plugin-transform-react-jsx@6.7.5
│ │ ├─┬ babel-helper-builder-react-jsx@6.7.5
│ └─┬ babel-plugin-transform-react-jsx-source@6.5.0
├─┬ react@0.14.8
├── react-addons-css-transition-group@0.14.8
├── react-addons-test-utils@0.14.8
├── react-dom@0.14.8
├─┬ react-loader@2.2.0
├─┬ react-redux@4.4.1
│ ├── hoist-non-react-statics@1.0.5
├── react-router@1.0.3
├─┬ react-select@1.0.0-beta12
│ └── react-input-autosize@0.6.10
│ ├─┬ react-json-tree@0.1.9
│ ├─┬ react-mixin@1.7.0
│ └── react-redux@3.1.2
│ └── react-lazy-cache@3.0.1
Samo
  • 8,202
  • 13
  • 58
  • 95
  • Are you very sure you only have one copy of React? That's still by far the most likely cause for this error. `npm ls` can usually help. – Sophie Alpert Apr 07 '16 at 07:09
  • Unless I'm blind, `npm ls` looks fine. Question updated with results. – Samo Apr 13 '16 at 04:40
  • Yes, that looks fine to me too. Only other common cause I know of is requiring with different cases (ex: `require('react')` vs `require('React')`) but since you're on Ubuntu I assume you have a case-sensitive filesystem so that wouldn't work at all. I'm stumped without more details. – Sophie Alpert Apr 13 '16 at 18:48
  • It could be coming from one of your npm dependencies that imports React and uses webpack for the build. That's my situation anyhow and I'm not yet sure how to resolve it. – donnapep Jul 13 '16 at 20:02
  • @donnapepI think you're right, I've realized more and more that how its built changes everything. – Samo Jul 14 '16 at 13:45

3 Answers3

0

Did you tried something like that :

  import ReactDOM from 'react-dom'
const start = _.isEmpty(ReactDOM.findDOMNode(this.refs.start).value) ? null : ReactDOM.findDOMNode(this.refs.start).value
    const end = _.isEmpty(ReactDOM.findDOMNode(this.refs.end).value) ? null : ReactDOM.findDOMNode(this.refs.end).value

I take your class and your refs works I do this simple test :

import React, { PropTypes, Component } from 'react'
import _ from 'lodash'

export default class RangeQuery extends React.Component {


    handleChange(ev) {


        const start = _.isEmpty(this.refs.start.value) ? null : this.refs.start.value
        const end = _.isEmpty(this.refs.end.value) ? null : this.refs.end.value
        console.log("start")
        console.log(start)


        console.log("end")
        console.log(end)
    }

    render() {
        return (
            <div>
                <input className="form-control" type="text" ref="start" onChange={::this.handleChange} placeholder="min" />
                <input className="form-control" type="text" ref="end"   onChange={::this.handleChange} placeholder="max" />
            </div>
        )
    }
}

The only difference is that I added : export default before the class tell me if it helps

fandro
  • 4,833
  • 7
  • 41
  • 62
  • The code in this handler doesn't make a difference. Simply having the `refs=` on the `input` tags is enough to cause the error. – Samo Apr 06 '16 at 21:35
0

It's not clear what the problem is and this still sounds like duplicate copies of React, but one workaround would be to use the function ref style which is be the preferred style anyway going forward:

<input ref={(node) => this.startInput = node} />

Then you can access this.startInput.value.

Sophie Alpert
  • 139,698
  • 36
  • 220
  • 238
  • Thanks, I will test this out. I wound up getting impatient and writing reducer logic to manage the component, but if this works it will be very handy to know. – Samo Apr 07 '16 at 13:29
0

this will definitely work

ReactDOM.findDOMNode(this.refs.start).value;
Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
nirav jobanputra
  • 337
  • 1
  • 3
  • 16