使用 React 外部组件和 webpack
Using react external components and webpack
我使用 webpack 并有一个简单的 React 应用程序,我想在其中使用 react-autosuggest 组件。当我想在我的应用程序中使用此组件时,出现错误:
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>
我尝试关注 this post 并添加:
alias: {
'react': path.join(__dirname, 'node_modules', 'react')
},
但没有用。
错误信息中包含对您的错误的解释:
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.
您不能将 ref
添加到顶级组件,就像您在 SuggestWrapper
:
中所做的那样
<Autosuggest suggestions={getSuggestions}
inputAttributes={inputAttributes}
ref={ () => { document.getElementById(inputId).focus(); } } />
要实现您想要的效果,您可以使用 componentDidMount
lifecycle hook:
componentDidMount() {
document.getElementById(inputId).focus();
}
来自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.
当您的包中有两个(或更多)React 副本时,可能会发生此错误。尝试 运行 npm ls react
看看是否发生这种情况。然后您可以尝试 npm dedupe
(或升级到 npm 3.x。它仍然是测试版,但它非常稳定并且 它会自动删除重复数据)。当然,你确实需要你使用的所有包都与你正在使用的 React 版本兼容,因为有多个 Reacts 会导致错误(和一个臃肿的包!)。
编辑:刚刚注意到您有一个用于 React 的脚本标签 并且 您正在导入它。去掉 react 脚本标签可能就是解决问题的全部!
我使用 webpack 并有一个简单的 React 应用程序,我想在其中使用 react-autosuggest 组件。当我想在我的应用程序中使用此组件时,出现错误:
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>
我尝试关注 this post 并添加:
alias: {
'react': path.join(__dirname, 'node_modules', 'react')
},
但没有用。
错误信息中包含对您的错误的解释:
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.
您不能将 ref
添加到顶级组件,就像您在 SuggestWrapper
:
<Autosuggest suggestions={getSuggestions}
inputAttributes={inputAttributes}
ref={ () => { document.getElementById(inputId).focus(); } } />
要实现您想要的效果,您可以使用 componentDidMount
lifecycle hook:
componentDidMount() {
document.getElementById(inputId).focus();
}
来自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.
当您的包中有两个(或更多)React 副本时,可能会发生此错误。尝试 运行 npm ls react
看看是否发生这种情况。然后您可以尝试 npm dedupe
(或升级到 npm 3.x。它仍然是测试版,但它非常稳定并且 它会自动删除重复数据)。当然,你确实需要你使用的所有包都与你正在使用的 React 版本兼容,因为有多个 Reacts 会导致错误(和一个臃肿的包!)。
编辑:刚刚注意到您有一个用于 React 的脚本标签 并且 您正在导入它。去掉 react 脚本标签可能就是解决问题的全部!