1

I use webpack and have a simple react application, where I want to use react-autosuggest component. When I want to use this component in my application I get error:

Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's `render` method). Try rendering this component inside of a new top-level component which will hold the ref.

index.jsx

var React = require('react')
var Autosuggest = require('react-autosuggest')

var autoCompleteItems = ['item1', 'item2', 'item3', 'item4', 'item5', 'item6'];

function getSuggestions(input, callback) {
  const escapedInput = utils.escapeRegexCharacters(input.trim());
  const lowercasedInput = input.trim().toLowerCase();
  const suburbMatchRegex = new RegExp('\\b' + escapedInput, 'i');
  const suggestions = autoCompleteItems
    .filter( suburbObj => suburbMatchRegex.test(suburbObj.suburb) )
    .sort( (suburbObj1, suburbObj2) =>
      suburbObj1.suburb.toLowerCase().indexOf(lowercasedInput) -
      suburbObj2.suburb.toLowerCase().indexOf(lowercasedInput)
    )
    .slice(0, 7)
    .map( suburbObj => suburbObj.suburb );

  setTimeout(() => callback(null, suggestions), 300);
}

class SuggestWrapper extends React.Component {
  render () {

    var inputId = 'input-example';

    const inputAttributes = {
      id: inputId,
      className: "form-control",
      defaultValue: '',
      placeholder: this.props.propertyName
    };

    return (

      <Autosuggest suggestions={getSuggestions}
                   inputAttributes={inputAttributes}
                   ref={ () => { document.getElementById(inputId).focus(); } } />

    );
  }
}


class App extends React.Component {
  render () {
    return (
      <div>
        <SuggestWrapper />
      </div>
    );
  }
}

React.render(<App />, document.getElementById('content'));

package.json

{
  "name": "react_modules",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "npm run serve | npm run dev",
    "serve": "./node_modules/.bin/http-server -p 8080",
    "dev": "webpack-dev-server -d --progress --colors --port 8090"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^5.8.24",
    "babel-loader": "^5.3.2",
    "bootstrap": "^3.3.5",
    "bower-webpack-plugin": "^0.1.8",
    "css-loader": "^0.18.0",
    "events": "^1.0.2",
    "extract-text-webpack-plugin": "^0.8.2",
    "file-loader": "^0.8.4",
    "http-server": "^0.8.0",
    "jquery": "^2.1.4",
    "jquery-ui": "^1.10.5",
    "less": "^2.5.1",
    "less-loader": "^2.2.0",
    "lodash": "^3.10.1",
    "node-sass": "^3.3.2",
    "object-assign": "^4.0.1",
    "path": "^0.11.14",
    "react": "^0.13.3",
    "react-autosuggest": "^1.18.3",
    "react-hot-loader": "^1.3.0",
    "sass-loader": "^2.0.1",
    "style-loader": "^0.12.3",
    "svg-sprite-loader": "0.0.3",
    "url-loader": "^0.5.6",
    "webpack": "^1.12.1",
    "webpack-dev-server": "^1.10.1"
  }
}

webpack.config.js

const BowerWebpackPlugin = require("bower-webpack-plugin");

module.exports = {
  entry: './src/index.jsx',
  output: {
    filename: 'bundle.js',
    sourceMapFilename: "[file].map",
    publicPath: 'http://localhost:8090/assets'
  },
  debug: true,
  devtool: 'inline-source-map',
  module: {
    loaders: [{
      test: /\.js[x]?$/,
      loaders: ['react-hot', 'babel'],
      exclude: /node_modules/
    }, {
      test: /\.scss$/,
      loaders: ['style', 'css?sourceMap', 'sass?sourceMap']
    }, {
      test: /\.less$/,
      loaders: ['style', 'css?sourceMap', 'less?sourceMap']
    }, {
      test: /\.css$/,
      loaders: ['style', 'css']
    }, {
      test: /\.woff$/,
      loader: "url-loader?limit=10000&mimetype=application/font-woff"
    }, {
      test: /\.woff2$/,
      loader: "url-loader?limit=10000&mimetype=application/font-woff2"
    }, {
      test: /\.(eot|ttf|svg|gif|png)$/,
      loader: "file-loader"
    }]
  },
  plugins: [
    new BowerWebpackPlugin()
  ],
  externals: {
    'react': 'React'
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  }
}

index.html

<!DOCTYPE html>
<html>
<head>
    <title>App</title>
    <!-- include react -->
    <script src="./node_modules/react/dist/react-with-addons.js"></script>
</head>
<body>
    <div id="content">
        <!-- this is where the root react component will get rendered -->
    </div>
    <!-- include the webpack-dev-server script so our scripts get reloaded when we make a change -->
    <!-- we'll run the webpack dev server on port 8090, so make sure it is correct -->
    <script src="http://localhost:8090/webpack-dev-server.js"></script>
    <!-- include the bundle that contains all our scripts, produced by webpack -->
    <!-- the bundle is served by the webpack-dev-server, so serve it also from localhost:8090 -->
    <script type="text/javascript" src="http://localhost:8090/assets/bundle.js"></script>
</body>
</html>

I tried to follow this post and add:

alias: {
      'react': path.join(__dirname, 'node_modules', 'react')
    },

but it didn't help.

Community
  • 1
  • 1
Matt
  • 8,195
  • 31
  • 115
  • 225

2 Answers2

5

This error can happen when you have two (or more) copies of React in your bundle. Try running npm ls react to see if this is happening. Then you might try npm dedupe (or upgrade to npm 3.x. It's still beta but it's pretty stable and it dedupes automatically). You really need all the packages you use to be compatible with the version of React you're using, of course, since having multiple Reacts causes errors (and a bloated bundle!).

Edit: just noticed you have a script tag for React and you're importing it. Taking out the react script tag might be all you need to fix the problem!

Adam DiCarlo
  • 6,655
  • 1
  • 17
  • 9
  • Thank you for response, yes I know that I probably get this error because I have in my bundle 2 copies of react, but i don't know how to deal with it. When I type `npm ls react` I get only `react@0.13.3`. I tried `npm dedupe` but nothing happen. When I tried to remove `script` tag in `index.html` I get error React is not defined. – Matt Sep 15 '15 at 10:06
  • Why are you declaring React as an external in your webpack config? – Adam DiCarlo Sep 15 '15 at 13:52
  • I think that's the problem here. Declaring as external means "fetch it out of a global variable." That's why *your* code is reliant on the script tag being there; *your* code is using the script tag copy of React, while react-autosuggest is using the React installed through npm. I would solve this by removing the script tag and removing the "externals" section of webpack.config.js. – Adam DiCarlo Sep 15 '15 at 14:32
  • Great answer! Thanks @AdamDiCarlo – Mohamed El Mahallawy Oct 16 '15 at 08:06
3

The explanation to your error is contained in the error message:

Uncaught Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. This usually means that you're trying to add a ref to a component that doesn't have an owner (that is, was not created inside of another component's render method). Try rendering this component inside of a new top-level component which will hold the ref.

You cannot add a ref to a top level component, as you are doing in your SuggestWrapper:

<Autosuggest suggestions={getSuggestions}
               inputAttributes={inputAttributes}
               ref={ () => { document.getElementById(inputId).focus(); } } />

To achieve what you want, you may use the componentDidMount lifecycle hook:

componentDidMount() {
    document.getElementById(inputId).focus();
}

From the docs:

At this point in the lifecycle, the component has a DOM representation which you can access via React.findDOMNode(this). The componentDidMount() method of child components is invoked before that of parent components.

gcedo
  • 4,811
  • 1
  • 21
  • 28
  • Thanks for your answer, I changed my code but still get the same error. [pastebin](http://pastebin.com/npqhnw5y) – Matt Sep 11 '15 at 12:08