Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

Reactのコードを大幅に省略できるClassFieldsの挙動をチェック

More than 1 year has passed since last update.

はじめに

ES2019で正式採用になるであろうClassFieldsの挙動について、よく考えず使っていたので、今更ではあるが、どのような動作をするかチェックした。

なお、現時点では stage3。tc39/proposal-class-fields

tl;dl

  • class fieldsはめっちゃ便利なのでどんどん利用していこう
  • 自分で手を動かすと理解が深まるよね

それどういうやつ

class Sample {
  x = 0; // public field
  #x = 0; // private field

  handleOnClick = (event) => {
    // ...
    this.x++;
  } // public field
}

以上のように、簡単にプロパティを定義できる。

チェックのための環境

最低限の動作をするプロジェクトを作成してテストする。
コードはここに公開済み。
https://github.com/tomoyamachi/minimum-react-dom-check

各種ライブラリ

基本ツールのバージョン情報は以下の通り。

    "react": "16.6.3",
    "react-dom": "16.6.3"
    "babel-core": "^6.26.3",
    "webpack": "^4.28.2",
    "webpack-dev-server": "^3.1.14",
    "why-did-you-update": "^1.0.6"

これにビルド時に必要なbabelのpreset/pluginを追加する。
今回は、class-properties用のpluginを入れる。

    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-preset-env": "^1.7.0",
    "babel-preset-react": "^6.24.1",

利用方法

$ git clone https://github.com/tomoyamachi/minimum-react-dom-check
$ yarn 
$ yarn start

http://localhost:8080 を開けばOK.

不要な再レンダリングが発生していると、 why-did-you-updateが教えてくれる。

検証内容

今回は、Publicなclass fieldsの動作確認とともに、バッドプラクティスと名高い、propsにbindや無名関数を直接いれちゃだめなやつも一緒にチェックした。
なお挙動確認のため、無理矢理stateを利用するなど、冗長になっている。

constructorでbindして渡す

class ButtonWithBind extends React.Component {
    constructor(props) {
        super(props);
        this.state = {type: "WithBind"}
        this.handleClick = this.handleClick.bind(this);
    }

    handleClick(message) {
        console.log(message, this.props.count)
        this.props.countUp();
    }

    render() {
        const {type} = this.state;
        return <Button type={type} key={type} handleClick={this.handleClick}/>;
    }
}

propsで直接bindして渡す

class ButtonWithoutBind extends React.Component {
    constructor(props) {
        super(props);
        this.state = {type: "WithoutBind"}
    }

    handleClick(message) {
        console.log(message, this.props.count)
        this.props.countUp();
    }

    render() {
        const {type} = this.state;
        return <Button type={type} key={type} handleClick={this.handleClick.bind(this)}/>;
    }
}

propsで即時関数として渡す

class ButtonDirectCall extends React.Component {
    constructor(props) {
        super(props);
        this.state = {type: "DirectCall"}
    }

    handleClick(message) {
        console.log(message, this.props.count)
        this.props.countUp();
    }

    render() {
        const {type} = this.state;
        return <Button type={type} key={type} handleClick={(message) => this.handleClick(message)}/>;
    }
}

propsで条件式を利用して渡す

class ButtonCondition extends React.Component {
    constructor(props) {
        super(props);
        this.state = {type: "Condition"}
        this.handleClick = this.handleClick.bind(this)
    }

    handleClick(message) {
        console.log(message, this.props.count)
        this.props.countUp();
    }

    render() {
        const {type} = this.state;
        return <Button type={type} key={type} handleClick={true && this.handleClick}/>;
    }
}

Class Fieldsを利用

class ButtonClassState extends React.Component {
    state = {type: "ClassState"}
    handleClick = (message) => {
        console.log(message, this.props.count)
        this.props.countUp();
    }

    render() {
        const {type} = this.state;
        return <Button type={type} key={type} handleClick={this.handleClick}/>;
    }
}

Buttonコンポーネントの作成

PureComponentにしないと、全パターンでre-renderされるので注意。

class Button extends React.PureComponent {
    state = {type: this.props.type}

    render() {
        const {type, handleClick} = this.props;
        return (
            <button onClick={() => handleClick(type)}>
                {type}
            </button>)
    }
};

そしてボタンを押してコンソールを確認する。

result.png

propsで直接bindして渡すpropsで即時関数として渡す が意味もなく再レンダリングされているのがわかる。
知識通りの挙動ではあるが、やはり手を動かして作ると実感が湧くのでいい。

結果 : ReactでClassFieldsを利用すると...

constructorがなくなり、コードがすっきりする

一番わかり易いメリットはこれ。
特に、関数に対してbindしてプロパティに渡すをしなくてよくなるのは助かる。

stateなどがプロパティに過ぎないことが理解しやすい

本来プロパティはクラスの属性なので、constructorの中で定義すると違和感があった。それもなくなる。
たとえば、 this.func = this.func.bind(this) はプロパティに関数を代入する行為に過ぎないはずだが、それが分かりづらい。黒魔術感が強い。
JS初心者の頃、この記述の必要性がわからず、もやもやした。

まとめ

Private Class Fieldsの挙動もチェックしたけど、後日記事にしていくつもり。
Static Class Fieldsをガンガン利用していこう。

tomoyamachi
Go / JavaScript / Docker / Python / K8s コンテナの導入〜セキュリティに関するコンサルティングをお受けしています。 コンテナのセキュリティツールDockle作者です。脆弱性スキャンツールのVuls, Trivyのコミッタです。 https://twitter.com/tomoyamachi
https://scrapbox.io/tomoyamachi/about_GoodWith
goodwith
技術支援や技術コンサルティングを行っています。Container Image LinterのDockleなどのOSS開発も行っています。
https://www.goodwith.tech/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away