LoginSignup
10
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2018-12-27

はじめに

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をガンガン利用していこう。

10
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
3