はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【React】Reactの勉強メモ①

JSXに式(値)を埋め込む

import React from 'react';
import ReactDOM from 'react-dom';

const SampleElement = () => {
  const dates = [{key: "1", value: "あたい", hoge: 10000}]

  return (
    <div>
      <h1>一覧</h1>
      <table>
        <thead>
          <tr><th>Key</th><th>Value</th><th>Hoge</th></tr>
        </thead>
        <tbody>
          <tr><td>{dates[0].key}</td><td>{dates[0].value}</td><td>{dates[0].hoge}</td></tr>
        </tbody>
      </table>
    </div>
  )
}

ReactDOM.render(
  <SampleElement />,
  document.getElementById('root')
)

コンポーネントの分割とProps

import React from 'react';
import ReactDOM from 'react-dom';

// コンポーネントに渡す引数をチェックしてくれるモジュール
import PropTypes from 'prop-types'

const SampleElement = () => {
  const dates = [
    {key: "1", value: "あたい", hoge: 10000}
  ]

  return (
    <div>
      <h1>一覧</h1>
      <table>
        <thead>
          <tr><th>Key</th><th>Value</th><th>Hoge</th></tr>
        </thead>
        <tbody>
          {/* パラメーターを渡す */}
          <SampleItem date={dates[0]} />
        </tbody>
      </table>
    </div>
  )
}

// パラメーターをpropsで受け取り
const SampleItem = (props) => {
  const {key, value, hoge} = props.date
  
  if(hoge > 0){
    return (
      <tr>
        <td>{key}</td>
        <td>{value}</td>
        <td>{hoge}</td>
      </tr>
    )
  } else {
    return (
      <h1>hoge : {hoge}</h1>
    )
  }
}

// SampleItemコンポーネントに渡すパラメータの型を宣言
// dataというobject型が渡ってくる
// isRequiredが付くと、dataパラメーターを渡さないとエラーになる
SampleItem.propTypes = {
  date: PropTypes.object.isRequired
}

ReactDOM.render(
  <SampleElement />,
  document.getElementById('root')
)

Propsはコンポーネント呼び出し側で設定した値の受け取り専用でコンポーネントの中で変更してはダメです。

条件演算子

const SampleItem = (props) => {
  const {key, value, hoge} = props.date
  
  return (
    <tr>
      <td>{key}</td>
      <td>{value}</td>
      <td>{hoge > 0 ? hoge : 0}</td>
    </tr>
  )
}

繰り返し

keyにはユニークな値を格納します。

const SampleElement = () => {
  const dates = [
    {key: "1", value: "あたい", hoge: 10000},
    {key: "2", value: "あたい", hoge: 10000},
    {key: "3", value: "あたい", hoge: 10000},
    {key: "4", value: "あたい", hoge: 10000},
    {key: "5", value: "あたい", hoge: 10000}
  ]

  return (
    <div>
      <h1>一覧</h1>
      <table>
        <thead>
          <tr><th>Key</th><th>Value</th><th>Hoge</th></tr>
        </thead>
        <tbody>
          {/* 繰り返し, keyはユニークな値を入れる */}
          {dates.map((date) => 
          <SampleItem date={date} key={date.key}/>
          )}
        </tbody>
      </table>
    </div>
  )
}

PropType

よく使いそうなもの。

PropType
配列 PropTypes.array
論理値 PropTypes.bool
関数 PropTypes.func
数値 PropTypes.number
オブジェクト PropTypes.object
文字列 PropTypes.string

必須パラメーターの場合は.isRequiredをつける。

コンポーネント作成

コンポーネントの作成には以下の2通りあります。

  • Componentクラスを継承したクラス
  • 関数のコンポーネント

Componentクラスを継承するクラスにrender()を定義すると、JSXを表示することができます。

class SampleComponent extends Component{
  render(){
    return (
      <div>
        <h1>サンプル</h1>
      </div>
    )
  }
}

Componentクラスは以下によって宣言されています。

import React, { Component } from 'react'

State

コンポーネント自体が状態や値を保ちたいとき、保存するのがStateです。

参照するときはthis.stateで、更新・代入をするときはthis.setState()を利用します。

class SampleComponent extends Component{
  constructor(){
    super()
    this.state = {hoge: 1, fuga: "value"}
  }

  set(){
    this.setState({hoge: this.state.hoge + 1, fuga: "value"})
  }

  render(){
    return (
      <div>
        <h1>サンプル {this.state.hoge} {this.state.fuga}</h1>
        <button onClick={() => this.set()}>ボタン</button>
      </div>
    )
  }
}

コンストラクターでpropsを受け取るバージョンもあります。

class SampleConponent extends Component {
  constructor(props) {
    super(props)
    // 何か初期化処理
  }

render()メソッドが呼び出されるのは以下の3つの場合です。

  • ブラウザにロードされた直後 (componentDidMount()が呼ばれる)
  • コンポーネントのPropsが変更されたとき (componentWillReceiveProps()が呼ばれる)
  • コンポーネント内でsetState()が実行され、Stateが変更されたとき

Controlled Components

イベント処理を使いプログラムで入力を管理するControlled Components方式。

  • inputタグのonChange属性を使い、入力されたデータを取得
  • 入力された値は基本的にはStateに保存
  • inputタグのvalue属性はStateの値を設定
class SampleComponent extends Component{
  constructor(){
    super()
    this.state = {date: ''}
  }

  onChangeValue(event){
    this.setState({[event.target.name] : event.target.value})
  }

  render(){
    return (
      <div>
        <p>{this.state.date}</p>
        <input type = "text" value={this.state.date} onChange={(event) => this.setState({date: event.target.value})} />
        <input type = "text" value={this.state.date} name="date" onChange={(e) => this.onChangeValue(e)} />
      </div>
    )
  }
}

Uncontrolled Components

  • 値を取得する必要があるタグにref属性を設定し、タグをアクセスするためのインスタンス変数に設定
  • 値を取得するには、上で保存したインスタンス変数を使い取得
class SampleComponent extends Component{

  constructor(){
    super()
    this.input = React.createRef()
  }

  onClickButton(){
    alert(this.input.current.value)
  }

  render(){
    return (
      <div>
        <input type="text" ref={this.input} />
        <button onClick={this.onClickButton}>Submit</button>
      </div>
    )
  }
}

フォーム要素

テキストエリア
class SampleComponent extends Component{
  constructor(props){
    super(props)
    this.state = {item: ''}
  }

  onChangeValue(event){
    this.setState({[event.target.name] : event.target.value})
  }

  render(){
    return (
      <div>
        <p>{this.state.date}</p>
        <textarea value={this.state.item} name="item" onChange={(e) => this.onChangeValue(e)} />
      </div>
    )
  }
}
セレクトボックス

選択肢が2個バージョン。

class SampleComponent extends Component{
  constructor(props){
    super(props)
    this.state = {isSelected : false}
  }

  render(){
    return (
      <div>
        <p>{this.state.isSelected ? "ON" : "OFF"}</p>
        <select value={this.state.isSelected ? "on" : "off"} onChange={(e) => this.setState({isSelected: e.target.value == "on"})}>
          <option value="on">ON</option>
          <option value="off">OFF</option>
        </select>
      </div>
    )
  }
}

配布されているReactコンポーネント

Material-UI

Material-UIMaterial Designに準拠したUIコンポーネントです。

Material UI is a library of React UI components that implements Google's Material Design.

Material UIは、GoogleのMaterial Designを実装したReact UIコンポーネントのライブラリです。

Overview - Material UI


インストールは以下のコマンドを打ちます。

$ npm install @mui/material @emotion/react @emotion/styled
import React, { Component } from 'react'
import ReactDOM from 'react-dom'

// Material-UI
import Button from '@mui/material/Button';

class SampleComponent extends Component{
  constructor(props){
    super(props)
  }

  render(){
    return (
      <div>
        <Button>text</Button>
        <Button variant="contained">contained</Button>
        <Button variant="outlined">outlined</Button>
      </div>
    )
  }
}

ReactDOM.render(
  <SampleComponent />,
  document.getElementById('root')
)
動作させた様子

簡単にいい感じの見た目を作ることができました。詳細は公式ドキュメントを参照。
mui.com

ルーティング

ルーティング対応する方法は以下の2種類あります。

  • pushState()メソッドを利用する。
  • URLのハッシュ(フラグメント, URLの#以降)に仮想的なURLを書く

React Router

reactrouter.com

インストール方法。

$ npm install react-router-dom
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter, Routes, Route, Link } from 'react-router-dom';


class SampleComponent extends Component{
  constructor(props){
    super(props)
  }

  render(){
    return (
      <div>
        <Link to="/">Home</Link>
        <Link to="/hoge">Hoge</Link>
        <Link to="/fuga">Fuga</Link>

        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/hoge" element={<Hoge />} />
          <Route path="/fuga" element={<Fuga />} />
          <Route path="*" element={<NoMatch />} />
        </Routes>
      </div>
    )
  }
}

const Home = () => {
  return (
    <div>
      <p>Home</p>
    </div>
  )
}

const Hoge = () => {
  return (
    <div>
      <p>Hoge</p>
    </div>
  )
}

const Fuga = () => {
  return (
    <div>
      <p>Fuga</p>
    </div>
  )
}

const NoMatch = () =>{
  return (
    <h2>このページは存在しません</h2>
  )
}

ReactDOM.render(
  <BrowserRouter>
    <SampleComponent />
  </BrowserRouter>,
  document.getElementById('root')
)

Fetch API

フェッチ API は(ネットワーク越しの通信を含む)リソース取得のためのインターフェイスを提供しています。 XMLHttpRequest と似たものではありますが、より強力で柔軟な操作が可能です。

developer.mozilla.org

class SampleComponent extends Component{
  constructor(props){
    super(props)
  }

  onClickButton(){
    // 犬の画像のURLが返ってくる
    fetch('https://dog.ceo/api/breeds/image/random')
      .then((response) => response.json())
      .then((json) => alert(json.message))
  }

  render(){
    return (
      <div>
        <button onClick={this.onClickButton}>Execute</button>
      </div>
    )
  }
}

ユニットテスト

Mocha

Mocha&Node.jsに標準で入っているassertを使ってユニットテストをします。

インストール。

$ npm install mocha --save-dev
$ npm install babel-register --save-dev

package.jsonに以下を追加します。

"scripts": {
    ...
    "test": "mocha --compilers js:babel-register --timeout 5000"
  },
  • --compilers js:babel-register:テストコードはbabelを利用する
  • --timeout 5000:強制終了時間をms指定。デフォルトは2000

it(説明文字列, テストコードの無名関数)のように指定します。

import assert from 'assert'

it("1+1=2", () => {
    assert.equal(1 + 1, 2)
})

グループ化にはdescribeを利用します。

import assert from 'assert'

describe('四則演算', () => {
    it("1+1=2", () => {
        assert.equal(1 + 1, 2)
    })

    it("1+2=3", () => {
        assert.equal(1 + 2, 3)
    })
})