我们先来看看下面这个组件:
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props)
this.textInput = null
}
handleClick() {
this.textInput.focus()
}
render() {
return (
<div>
<input type="text" ref={(node) => {this.textInput = node}}/>
<input type="button" value="foucs" onClick={this.handleClick.bind(this)}/>
</div>
)
}
}
export default App
该组件的功能是点击focus
按钮会使input
框聚焦,我在ref
中使用了箭头函数回调,在onClick
中使用bind
绑定函数,但我们很不建议在jsx
属性中使用箭头函数和bind
,原因有以下两点:
- 每次渲染将重新创建新的函数,每当创建一个函数时,就会对前一个函数进行垃圾回收,这样就会对垃圾回收器造成负担。
- 属性中的箭头函数会影响渲染过程:当你使用了
PureComponent
,或者自己实现了shouldComponentUpdate
方法,使用对象比较的方式来决定是否要重新渲染组件,那么组件属性中的箭头函数就会让该方法永远返回真值,引起不必要的重复渲染。
那么,我们如何来解决上面问题呢?很简单,将箭头函数和bind
外移就行了
import React from 'react'
class App extends React.Component {
constructor(props) {
super(props)
this.textInput = null
this.setTextInputRef = element => {
this.textInput = element
}
}
handleClick = () => {
this.textInput.focus()
}
render() {
return (
<div>
<input type="text" ref={this.setTextInputRef}/>
<input type="button" value="foucs" onClick={this.handleClick}/>
</div>
)
}
}
export default App
我们将setTextInputRef
和handleClick
都是用箭头函数实现,就避免了去绑定this
,这是 JS
中的一个实验中的特性,这意味着她还没有被 ECMAScript
的标准所采纳,不过在它被采纳之前,你可以配置 babel
,使用 @babel/plugin-proposal-class-properties
来转换它。我们来配置试试:
.babelrc
配置如下:
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
],
"plugins": [
"@babel/plugin-transform-runtime",
"@babel/plugin-proposal-class-properties"
]
}
执行npx babel ./src/index.js -o ./dist/compiled.js
,然后打开compiled.js
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _assertThisInitialized2 = _interopRequireDefault(require("@babel/runtime/helpers/assertThisInitialized"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var App =
/*#__PURE__*/
function (_React$Component) {
(0, _inherits2["default"])(App, _React$Component);
function App(props) {
var _this;
(0, _classCallCheck2["default"])(this, App);
_this = (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(App).call(this, props));
(0, _defineProperty2["default"])((0, _assertThisInitialized2["default"])(_this), "handleClick", function () {
_this.textInput.focus();
});
_this.textInput = null;
_this.setTextInputRef = function (element) {
_this.textInput = element;
};
return _this;
}
(0, _createClass2["default"])(App, [{
key: "render",
value: function render() {
return _react["default"].createElement("div", null, _react["default"].createElement("input", {
type: "text",
ref: this.setTextInputRef
}), _react["default"].createElement("input", {
type: "button",
value: "foucs",
onClick: this.handleClick
}));
}
}]);
return App;
}(_react["default"].Component);
var _default = App;
exports["default"] = _default;
我们可以看到类方法的箭头函数被成功转译。就不需要担心支不支持了。
既然我们建议在jsx
属性中不要使用箭头函数和绑定,但是我们可能在编写代码过程中由于习惯写了箭头函数或者绑定,有没有什么工具可以帮助我们检测呢,显然eslint
可以做到这一点,我们只需要安装eslint-plugin-react
插件,并配置react/jsx-no-bind
规则,详细如下:
module.exports = {
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 2018,
"sourceType": "module"
},
"plugins": [
"react"
],
"rules": {
"react/jsx-no-bind": ["error", {
"ignoreDOMComponents": false,
"ignoreRefs": false,
"allowArrowFunctions": false,
"allowFunctions": false,
"allowBind": false
}]
}
};
当我们执行npx eslint ./src/index.js
,将会报下面错误
参考:
https://www.freecodecamp.org/news/react-pattern-extract-child-components-to-avoid-binding-e3ad8310725e/
https://stackoverflow.com/questions/36677733/why-shouldnt-jsx-props-use-arrow-functions-or-bind
https://blog.csdn.net/zjerryj/article/details/82700571
https://www.w3ctech.com/topic/2096
https://babeljs.io/docs/en/babel-plugin-proposal-class-properties
https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。