ホームページ  >  記事  >  ウェブフロントエンド  >  React の関数コンポーネントとクラス コンポーネントの違いは何ですか?

React の関数コンポーネントとクラス コンポーネントの違いは何ですか?

青灯夜游
青灯夜游オリジナル
2020-12-03 09:58:1620944ブラウズ

違い: 1. function コンポーネントは props オブジェクトを受け取り、react 要素を返す純粋な関数ですが、class コンポーネントは React.Component を継承し、react 要素を返すレンダー関数を作成する必要があります。 2. 関数コンポーネントにはライフサイクルと状態がありませんが、クラスコンポーネントにはライフサイクルと状態があります。

React の関数コンポーネントとクラス コンポーネントの違いは何ですか?

この記事の動作環境: Windows7 システム、Dell G3 コンピューター、react17.0.1 バージョン。

この記事では、関数コンポーネントとクラスコンポーネントの違いは何か、コーディング中にどのように選択すべきかについて説明します。

コンポーネントを定義する最も簡単な方法は、JavaScript 関数を使用することです。

import React from 'react'
const Welcome = (props) => {
  return <h1>welcome, {props.name}</h1>
}
export default Welcome

この関数は、props オブジェクトを受け取り、## を返します。 #react Elements

ES6 class 構文を使用してコンポーネントを記述することもできます。

import React from 'react'
class Welcome extends React.Component {
  constructor(props) {
    super(props)
  }
  render() {
    return <h1>welcome, {this.props.name}</h1>
  }
}

export default Welcome
2 つのバージョンは同等であり、同じ出力が得られます。では、どの実装方法を選択すればよいのでしょうか?以下で比較してみましょう

1. 構文

両者の最も明らかな違いは、構文上、関数コンポーネントが純粋関数であることです。

props オブジェクトを受け取り、react 要素を返します。クラス コンポーネントは、React.Component を継承し、react 要素を返す render 関数を作成する必要があります。これにより、同じ効果が得られますが、より多くのコードが必要になります。 . .

さらに詳しく見て、

babel7 を使用してそれぞれを翻訳してみましょう。 関数コンポーネントの翻訳結果:

"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports["default"] = void 0;

var _react = _interopRequireDefault(require("react"));

var Welcome = function Welcome(props) {
  return _react["default"].createElement("h1", null, "welcome, ", props.name);
};

var _default = Welcome;
exports["default"] = _default;
クラスコンポーネントの翻訳結果:

"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 _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));

var _react = _interopRequireDefault(require("react"));

var Welcome =
/*#__PURE__*/
function (_React$Component) {
  (0, _inherits2["default"])(Welcome, _React$Component);

  function Welcome(props) {
    (0, _classCallCheck2["default"])(this, Welcome);
    return (0, _possibleConstructorReturn2["default"])(this, (0, _getPrototypeOf2["default"])(Welcome).call(this, props));
  }

  (0, _createClass2["default"])(Welcome, [{
    key: "render",
    value: function render() {
      return _react["default"].createElement("h1", null, "welcome, ", this.props.name);
    }
  }]);
  return Welcome;
}(_react["default"].Component);

var _default = Welcome;
exports["default"] = _default;
クラスコンポーネントが

ES5 に変換された後のコードがますます長くなっていることがわかりますが、これはそれらを区別する主な要素ではなく、理解するだけです。

2. 状態管理

関数コンポーネントは純粋関数であるため、関数コンポーネントで

setState()## を使用することはできません。コンポーネント #。これが、関数コンポーネントがステートレス コンポーネントと呼ばれる理由です。 コンポーネントで

state

を使用する必要がある場合は、クラス コンポーネントを作成するか、state を親コンポーネントにプロモートして props を渡すことを選択できます。 オブジェクトが子コンポーネントに渡されました。

3. ライフ サイクル フック

state

と同じ理由で、関数コンポーネントでライフ サイクル フックを使用することはできません。同様に、すべてのライフサイクル フックは、継承された React.Component から取得されます。 したがって、ライフサイクルフックを使用したい場合は、クラスコンポーネントを使用する必要があります。

注:

hooksreact16.8 バージョンに追加され、関数コンポーネントで useState フックを使用できるようになりました。 stateuseEffect フックを使用してライフサイクル関数を使用します。したがって、ポイント 2 と 3 は相違点ではありません。このリビジョンから、作者が機能コンポーネントをより重視していることがわかり、react チームは、react の後続のバージョンでは機能コンポーネントのパフォーマンスが向上すると述べています。

4. メソッドの呼び出し

SayHi

が関数の場合、Reactそれを呼び出す必要があります: <pre class="brush:php;toolbar:false">// 你的代码  function SayHi() {      return &lt;p&gt;Hello, React&lt;/p&gt;  }  // React内部  const result = SayHi(props) // » &lt;p&gt;Hello, React&lt;/p&gt;</pre>

SayHi

がクラスの場合、React は最初に new 演算子を使用してインスタンス化してから呼び出す必要があります。インスタンスを生成するための render メソッド: <pre class="brush:php;toolbar:false">// 你的代码  class SayHi extends React.Component {      render() {          return &lt;p&gt;Hello, React&lt;/p&gt;      }  }  // React内部  const instance = new SayHi(props) // » SayHi {}  const result = instance.render() // » &lt;p&gt;Hello, React&lt;/p&gt;</pre>

ご想像のとおり、関数コンポーネントの再レンダリングでは、コンポーネント メソッドが再度呼び出され、新しい

react が返されます。 要素、クラス コンポーネント 再レンダリングは、新しいコンポーネント インスタンスを new し、その後 render クラス メソッドを呼び出して react 要素を返します。クラスコンポーネント # の this## が変数

#5 である理由を説明します。

#This をレンダリングするときに値を取得します。が最大の違いですが、無視されることがよくあります。

次のコンポーネントについて考えてみましょう:

function ProfilePage(props) {
  const showMessage = () => {
    alert('Followed ' + props.user);
  }

  const handleClick = () => {
    setTimeout(showMessage, 3000);
  }

  return (
    <button>Follow</button>
  )
}

UserProfile

コンポーネントは非常に単純で、

setTimeout## を使用する フォロー ボタンだけです。 # ネットワークリクエストをシミュレートします。ユーザーがこのボタンをクリックすると、警告ボックスがポップアップ表示されます。 props.user'Dan' の場合、3 秒後に 'Followed Dan' と表示されます。 これをクラスとして記述するにはどうすればよいでしょうか?素朴な翻訳は次のようになります。 <pre class="brush:php;toolbar:false">class ProfilePage extends React.Component {   showMessage() {     alert('Followed ' + this.props.user);   }   handleClick() {     setTimeout(this.showMessage.bind(this), 3000);   }   render() {     return &lt;button&gt;Follow&lt;/button&gt;   } }</pre> これら 2 つのコード スニペットは同等であると考えるのが一般的です。人々は、その意味に気づかずに、これらのパターン間を自由にリファクタリングすることがよくあります。

ただし、これら 2 つのコード スニペットはまったく異なります。

よく見てください。違いがわかりますか?

次の順序で フォロー ボタンを操作します。

最初に ## の フォロー

ボタン
  1. をクリックします。 #3s前にドロップダウン選択のオプションを変更します。
  2. ポップアップ警告ボックスの内容を読んでください。
  3. 関数の間に違いがあることがわかります。コンポーネントとクラスコンポーネント:
  4. 函数组件:按上面所列的三个步骤操作时,当用户在3s前更改下拉选择框的选项时,h1的用户名会立马改变,而3s后弹出的警告框中的用户名并不会改变

    类组件:按上面所列的三个步骤操作时,当用户在3s前更改下拉选择框的选项时,h1中的用户名会立马改变,而3s后弹出的警告框中的用户名也会改变

    React の関数コンポーネントとクラス コンポーネントの違いは何ですか?

    React の関数コンポーネントとクラス コンポーネントの違いは何ですか?

    那么,为什么我们的类示例会这样表现呢?

    让我们仔细看一下showMessage类中的方法:

    showMessage() {
        alert('Followed ' + this.props.user);
      }

    showMessage方法中读取了this.props.user(也是我们要输出的用户名称)。而React中的props是不可变的,但是this是可变的,而且是一直是可变的。这也是类组件中this的目的。React自身会随着时间的推移对this进行修改,以便你可以在render函数或生命周期中读取新的版本。

    因此,如果组件在请求重新渲染时,this.props将会改变。showMessage方法会从新的props中读取user。你所看到的效果也正是因为这个原因。

    React中的组件,UI在概念上可以理解是程序当前状态的函数,那么事件处理就是让UI的渲染结果一部分一部分可视化输出。我们的事件处理程序属于具有特定propsstate的特定渲染。但是,当回调超时的话,this.props就会打破这种联系。示例中的showMessage方法在回调时没有绑定到任何特定的渲染,因此它会丢失真正的props

    那么我们有没有一种较好的方式可以使用正确的props来修复rendershowMessage回调之间的联系。我们可以在事件发生的早期,将this.props传递给超时完成的处理程序来尝试着解决这个问题。这种解决方式属于闭包的范畴。

    class ProfilePage extends React.Component {
      showMessage(user) {
        alert('Followed ' + user);
      }
    
      handleClick() {
        cosnt {user} = this.props
        setTimeout(this.showMessage.bind(this, user), 3000);
      }
    
      render() {
        return <button>Follow</button>
      }
    }

    我们使用闭包机制将上一状态的值保存下来待showMessage方法调用。即使this.props发生变化,但并不改变user

    这种方法虽然解决我们前面所提到的问题,但是这种方法代码会随着props的个数增加,代码也会变得更加冗余也易于出错。如果我们也需要访问state。如果showMessage调用另一个方法,该方法会读取this.props.somethingthis.state.something。我们又会碰到同样的问题。所以我们必须通过this.props作为showMessage的参数来修复它们之间存在的问题。

    但这么做会破坏类提供的特性。也令人难于记住或执行。另外,在handleClick中内联alert中的代码并不能解决更大的问题。我们希望以一种允许代码分解成更多方法的方式来构造代码,同时还可以读取与其相关的render所对应的propsstate

    或许,我们可以在类的构造函数中绑定这些方法:

    class ProfilePage extends React.Component {
      render() {
        // 获取props
        cosnt props = this.props
        
        // 它们不是类方法
        const showMessage = () => {
            alert('Followed ' + props.user);
        }
        
        const handleClick = () => {
            setTimeout(showMessage, 3000)
        }
        
        return <button>Follow</button>
      }
    }

    这样一来,函数组件和类组件所达到的效果都一样了。在类组件中可以捕获渲染时的props。效果上看上去是一样了,但看起来怪怪的。如果在类组件中的render中定义函数而不是使用类方法,那么还有使用类的必要性?

    更多编程相关知识,请访问:编程课程!!

以上がReact の関数コンポーネントとクラス コンポーネントの違いは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。