2

我们先来看看下面这个组件:

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原因有以下两点:

  1. 每次渲染将重新创建新的函数,每当创建一个函数时,就会对前一个函数进行垃圾回收,这样就会对垃圾回收器造成负担。
  2. 属性中的箭头函数会影响渲染过程:当你使用了 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

我们将setTextInputRefhandleClick都是用箭头函数实现,就避免了去绑定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,将会报下面错误
1.png

参考:
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


记得要微笑
1.9k 声望4.5k 粉丝

知不足而奋进,望远山而前行,卯足劲,不减热爱。