react-爲何在jsx屬性中不建議使用箭頭函數和綁定

咱們先來看看下面這個組件:node

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緣由有如下兩點:react

  1. 每次渲染將從新建立新的函數,每當建立一個函數時,就會對前一個函數進行垃圾回收,這樣就會對垃圾回收器形成負擔。
  2. 屬性中的箭頭函數會影響渲染過程:當你使用了 PureComponent,或者本身實現了 shouldComponentUpdate 方法,使用對象比較的方式來決定是否要從新渲染組件,那麼組件屬性中的箭頭函數就會讓該方法永遠返回真值,引發沒必要要的重複渲染。

那麼,咱們如何來解決上面問題呢?很簡單,將箭頭函數和bind外移就好了git

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來轉換它。咱們來配置試試:es6

.babelrc配置以下:github

{
  "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.jsbabel

"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.pngui

參考:
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
http://www.javashuo.com/article/p-vurzalix-kb.html
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.mdthis

相關文章
相關標籤/搜索