Make it to make it

いろいろ作ってアウトプットするブログ

React学習1(Prettier+ESLint, state, computed/method)

React復習がてら要点をまとめていく。

環境構築

Starter Kit

create-react-appをグローバルでインストールして、スターターファイルを立ち上げる。

create-react-app counter-app

ちなみに、スターター系は公式ドキュメントStarter Kits – Reactにもまとめられている。

Prettier + ESLintをStandardJSに沿ってVSCodeで設定

必要なパッケージをインストール。

yarn add -D \
eslint \
prettier \
eslint-config-prettier \
eslint-config-standard \
eslint-plugin-import \
eslint-plugin-node \
eslint-plugin-prettier \
eslint-plugin-promise \
eslint-plugin-standard

次に、VSCodeの設定ファイルであるsettings.jsonに記述を追加する。

{
  "eslint.autoFixOnSave": true,
}

.eslintrc.jsonを作成して記述を追加

{
  "extends": [
    "standard",
    "prettier"
  ],
  "rules": {
    "prettier/prettier": [
      "error",
      {
        "singleQuote": true,
        "semi": false,
        "trailingComma": "all"
      }
    ]
  },
  "parser": "babel-eslint",
  "plugins": [
    "prettier"
  ]
}

最初のコンポーネント

プラスボタンを押して、カウンターの数値が上がっていくというシンプルなコンポーネントを作る。 ゼロのときはZeroが表示されて、1以上は数値を画面表示する。次いでにリスト表示もしてみる。

  • 何かを返却するファンクションに関しては()を末尾に付けてJSX内に記述する。
  • イベントハンドラにメソッドを記述した場合は()は書かない。
  • イベントハンドラにメソッドを記述した場合に、メソッドの書き方に応じてthisをどうバインドさせるかというものに複数の書き方がある。
    • constructorを記述して書くパターン
    • アローファンクションで書くパターン

state, method, listのレンダリング

Counter.jsx(constructorを記述して書くパターン)

import React, { Component } from 'react'

class Counter extends Component {
  state = {
    count: 0,
    tags: ['tag1', 'tag2', 'tag3'],
  }

  constructor() {
    super()
    this.handleIncrement = this.handleIncrement.bind(this)
  }

  render() {
    return (
      <>
        {this.state.tags.length === 0 && 'Please create a new tag!'}
        {this.renderTags()}
      </>
    )
  }

  handleIncrement() {
    console.log('Increment clicked!', this)
    this.setState({
      count: this.state.count + 1,
    })
  }

  renderTags() {
    if (this.state.tags.length === 0) return <p>There are no items!</p>

    return (
      <>
        <span className={this.getBadgeClasses()}>{this.formatCount()}</span>
        <button
          onClick={this.handleIncrement}
          className="btn btn-secondary btn-sm"
        >
          Increment
        </button>
        <ul>
          {this.state.tags.map(tag => (
            <li key={tag}>{tag}</li>
          ))}
        </ul>
      </>
    )
  }

  getBadgeClasses() {
    let classes = 'badge m-2 badge-'
    classes += this.state.count === 0 ? 'warning' : 'primary'
    return classes
  }

  formatCount() {
    const { count } = this.state
    return count === 0 ? <h1>Zero</h1> : count
  }
}

export default Counter

Counter.jsx(アローファンクションで書くパターン)

import React, { Component } from 'react'

class Counter extends Component {
  state = {
    count: 0,
    tags: ['tag1', 'tag2', 'tag3'],
  }

  render() {
    return (
      <>
        {this.state.tags.length === 0 && 'Please create a new tag!'}
        {this.renderTags()}
      </>
    )
  }

  handleIncrement = () => {
    console.log('Increment clicked!', this)
    this.setState({
      count: this.state.count + 1,
    })
  }

  renderTags() {
    if (this.state.tags.length === 0) return <p>There are no items!</p>

    return (
      <>
        <span className={this.getBadgeClasses()}>{this.formatCount()}</span>
        <button
          onClick={this.handleIncrement}
          className="btn btn-secondary btn-sm"
        >
          Increment
        </button>
        <ul>
          {this.state.tags.map(tag => (
            <li key={tag}>{tag}</li>
          ))}
        </ul>
      </>
    )
  }

  getBadgeClasses() {
    let classes = 'badge m-2 badge-'
    classes += this.state.count === 0 ? 'warning' : 'primary'
    return classes
  }

  formatCount() {
    const { count } = this.state
    return count === 0 ? <h1>Zero</h1> : count
  }
}

export default Counter

メソッドに引数を渡したい場合

引数を渡してイベントハンドラをJSX内に記述したい場合は、アローファンクションで記述する。

Counter.jsx

import React, { Component } from 'react'

class Counter extends Component {
  state = {
    count: 0,
    tags: ['tag1', 'tag2', 'tag3'],
    product: { id: 1 },
  }

  render() {
    return (
      <>
        {this.state.tags.length === 0 && 'Please create a new tag!'}
        {this.renderTags()}
      </>
    )
  }

  handleIncrement = product => {
    console.log(product)
    this.setState({
      count: this.state.count + 1,
    })
  }

  renderTags() {
    const { product } = this.state

    if (this.state.tags.length === 0) return <p>There are no items!</p>

    return (
      <>
        <span className={this.getBadgeClasses()}>{this.formatCount()}</span>
        <button
          onClick={() => this.handleIncrement(product)}
          className="btn btn-secondary btn-sm"
        >
          Increment
        </button>
        <ul>
          {this.state.tags.map(tag => (
            <li key={tag}>{tag}</li>
          ))}
        </ul>
      </>
    )
  }

  getBadgeClasses() {
    let classes = 'badge m-2 badge-'
    classes += this.state.count === 0 ? 'warning' : 'primary'
    return classes
  }

  formatCount() {
    const { count } = this.state
    return count === 0 ? <h1>Zero</h1> : count
  }
}

export default Counter