クモのようにコツコツと

フロントエンドエンジニア イイダリョウの技術ブログ。略称「クモコツ」

【React】Reactプロジェクトでステートを事始め(setState()、タイマー処理、イベント処理)

Reactの続きです。前回はReactプロジェクトでクラスコンポーネントを作成しました。今回はステートを事始ます。setState()による値の変更と、setInterbal()によるタイマー処理、onClick属性のイベント処理をやってみます。それではいきましょう!

【目次】

※参考:前回記事
【React】Reactプロジェクトでクラスコンポーネント作成、入れ子、ファイル分割まで - クモのようにコツコツと

※参考:Reactでやってみたことまとめ qiita.com

前回のおさらい

今回も掌田さんの「React.js & Next.js入門」を参考に進める。

React.js & Next.js超入門

React.js & Next.js超入門

前回はコンポーネントの属性に値を入れて固有の内容にした。

Imgコンポーネントを

  render() {
    return  <img src={logo} className="App-logo" alt="logo" style={this.style} />
  }

Reactコンポーネントに入れ子にして

        <div className="App-images">
          <Img align="left" />
          <Img align="center" />
          <Img align="rignt" />
        </div>

aline属性で値を設定

さらにReactコンポーネントのレンダリングで

ReactDOM.render(
  <React.StrictMode>
    <App title="クモのようにコツコツと" message="Reactコンポーネントもコツコツと!" />
  </React.StrictMode>,
  document.getElementById('root')
);

title属性とmessage属性に値を設定

※参考:【React】Reactプロジェクトでクラスコンポーネント作成、入れ子、ファイル分割まで - クモのようにコツコツと

ステートで「状態管理」をする!

こうした値は「Read only」で変更をすることができない。

それを変更するのが「ステート」による状態管理!

state と props の違いは何ですか?

どちらもレンダー結果に影響を及ぼす情報を持ってはいますが、ある重要な一点が異なっています。つまり、props は(関数引数のように)コンポーネントへ渡されるのに対し、state は(関数内で宣言された変数のように)コンポーネントの内部で制御されます。

※参考:コンポーネントの state – React

propsと違い、値を更新することができるのがstate

「状態管理」についてはVue.jsのVuexをやるときに調べた。このときReactには「Redux」があることを知った。

※参考:【Vue.js】Vuexの「状態管理」はいったい何の状態を管理しているのか調べた - クモのようにコツコツと

今回の段階ではまだReduxは使わずにstateを利用する。

App.js(変更前)

App.js(変更前)

import React from 'react';
import Img from './Img';
import './App.css';

class App extends React.Component {
  constructor(props) {
    super();
    this.title = props.title;
    this.message = props.message;
  }
  render() {
    return <div className="App">
      <header className="App-header">
        <div className="App-images">
          <Img align="left" />
          <Img align="center" />
          <Img align="rignt" />
        </div>
        <h1>{this.title}</h1>
        <p>{this.message}</p>
      </header>
    </div>
  }
}

export default App;

ここでAppコンポーネントを設定している。

コンストラクタにステートの初期値を設定

ステートの初期値設定

ステートの初期値はこのような書き方をする。

this.state = {キー: 値}

this.stateの中はオブジェクト(連想配列)

コンストラクタを修正

コンストラクタにステートの初期値を設定する

  constructor(props) {
    super();
    this.state = {
      message: 'Reactステートもコツコツと!'  
    }
  }

JSXにはstatepropsを設定して見る。

        <h1>初めてのステート</h1>
        <p>{this.state.message}</p>
        <p>{this.props.message}</p>

なお、index.jsx上のAppコンポーネントには前回設定したmessage属性がある。

ReactDOM.render(
  <React.StrictMode>
    <App message="Reactコンポーネントもコツコツと!" />
  </React.StrictMode>,
  document.getElementById('root')
);

ブラウザ挙動

ターミナルでReact起動!

npm start

おお、表示された! f:id:idr_zz:20200525194553j:plain 上の行はstate、下の行はpropsが読み込まれている。propsの値はAppコンポーネントのmessage属性の値。stateではこれは無視される。

App.js全体

import React from 'react';
import Img from './Img';
import './App.css';

class App extends React.Component {
  constructor(props) {
    super();
    this.state = {
      message: 'Reactステートもコツコツと!'  
    }
  }
  render() {
    return <div className="App">
      <header className="App-header">
        <div className="App-images">
          <Img align="left" />
          <Img align="center" />
          <Img align="rignt" />
        </div>
        <h1>初めてのステート</h1>
        <p>{this.state.message}</p>
        <p>{this.props.message}</p>
      </header>
    </div>
  }
}

export default App;

setState()でステートを変更

setState()を使うと値を変更できる。下記の2つの方法がある。

// オブジェクトを渡す
this.setState({
    キー: 値
});

// 関数渡す
this.setState((state) => ({
    キー: 値
}));

オブジェクト(連想配列)の方が見た目はシンプルだが、ステートを意図通りに変更されない可能性があるため、関数を渡す方がいい。

setState() は、引数に オブジェクトが渡された場合 に、 state を即時にアップデートすることを 保証しません。 Reactは、パフォーマンスを高めるため、複数の setState() を単一の更新にバッチするためです。 そのため、1つの関数内で state の更新を連続的に行うときは、引数に関数を渡しましょう。

※参考:React の setState() の引数が関数の場合とオブジェクトの場合の違いについて整理する - Qiita

※参考:state とライフサイクル – React

タイマー処理(setInterval())でステートを変更する

タイマー処理

タイマー処理setInterval()を使って時間毎に変化させる。

setInterval(() => { 処理 }, 時間);

下記はJSXの時に使ったsetInterval()

※参考:【React】JSXレンダリングとタイマー処理(setInterval())の関係を理解する - クモのようにコツコツと

App.js修正

App.jsのコンストラクタを修正

  constructor(props) {
    super(props);
    this.state = {
      msg1: "羊が",
      num: 1,
      msg2: "匹"
    };
    let count = setInterval(() => {
      this.setState((state) => ({
        message: state.msg1 + state.num++ + state.msg2
      }));
    }, 1000);
  }
  • this.statemsg1nummsg2の3つの初期値を設定。numは数値で三つ合わせると「羊が1匹」になる
  • 変数countsetInterval()のタイマー処理を設定
  • setInterval()第1引数は無名関数でここに処理を書く
    this.setState()に関数を渡す。引数はstate
  • messageの値をstatemsg1numに1を加算 、msg2にする
  • setInterval()の第2引数は時間で1000ミリ秒=1秒ごとに更新する

ブラウザ挙動

ブラウザを開くと f:id:idr_zz:20200526194215j:plain 変わった!1秒ごとに羊を数えている!

App.js全体

import React from 'react';
import Img from './Img';
import './App.css';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      msg1: "羊が",
      num: 1,
      msg2: "匹"
    };
    let count = setInterval(() => {
      this.setState((state) => ({
        message: state.msg1 + state.num++ + state.msg2
      }));
    }, 1000);
  }

  render() {
    return <div className="App">
      <header className="App-header">
        <div className="App-images">
          <Img align="left" />
          <Img align="center" />
          <Img align="rignt" />
        </div>
        <h1>初めてのステート</h1>
        <p>{this.state.message}</p>
        <p>{this.props.message}</p>
      </header>
    </div>
  }
}

export default App;

クリックイベントでステートを変更

イベント処理

次にクリックイベントでステートを変更して見る。

クリックイベントはコンポーネントにonClick属性を入れる。

<タグ on○○={this.○}>

下記はJSXのときにやったイベント処理。この時はinputタグにonChange属性を入れている。*1

※参考:【React】イベント属性を使ってみた(onChangeとonClick) - クモのようにコツコツと

App.js修正

今回はbuttonタグにonClick属性を入れて見る所存。*2

App.jsのJSXレンダリング部分を修正

<button onClick={this.countUp}>数える!</button>
  • buttonタグのonClick属性にcountUpメソッドを設定

イベントを実行するためにはメソッドをthisにバインドする必要がある。

this.○○ = this.○○.bind(this);

※参考:イベント処理 – React

コンストラクタ部分を修正

  constructor(props) {
    super(props);
    this.state = {
      msg1: "羊が",
      num: 1,
      msg2: "匹",
      message:  "羊が何匹?"
    };
    this.countUp = this.countUp.bind(this);
  }
  • messageの初期値「羊が何匹?」を用意
  • countUpメソッドをthisにバインドする

今回はクリックして初めてmessageが表示される仕組みのため、messageの初期値が必要だった。

次にcountUp()メソッドを設定する

  countUp(e) {
    this.setState((state) => ({
      num: state.num +1,
      message: state.msg1 + state.num + state.msg2
    }));
  }
  • countUp()の定義、引数はe
  • this.setState()に関数を渡す。引数はstate
    statenumの数値を1つ増やす
    messagestatemsg1nummsg2にする。

ブラウザ挙動

ブラウザを見ると、初期値の「羊が何匹?」が表示された! f:id:idr_zz:20200527072254j:plain

クリックすると「1匹」になる! f:id:idr_zz:20200527072258j:plain

もう一回クリックすると「2匹」になる! f:id:idr_zz:20200527072303j:plain

App.js全体

import React from 'react';
import Img from './Img';
import './App.css';

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      msg1: "羊が",
      num: 1,
      msg2: "匹",
      message:  "羊が何匹?"
    };
    this.countUp = this.countUp.bind(this);
  }

  countUp(e) {
    this.setState((state) => ({
      num: state.num +1,
      message: state.msg1 + state.num + state.msg2
    }));
  }


  render() {
    return <div className="App">
      <header className="App-header">
        <div className="App-images">
          <Img align="left" />
          <Img align="center" />
          <Img align="rignt" />
        </div>
        <h1>初めてのステート</h1>
        <p>{this.state.message}</p>
        <button onClick={this.countUp}>数える!</button>
      </header>
    </div>
  }
}

export default App;

最後に

ステートの事始めとタイマー処理、イベント処理を体験しました。ブラウザの挙動自体はこれまでと同じような内容です。

しかしpropsではコンポーネントの中に属性として値を入れていたので設定値がいろんなところに分散しそうです。それがステートで一元管理できるので、規模が大きくなるほどにメリットを感じることができそうです。

次回も引き続きステートを扱う予定です。それではまた!


※参考:Reactでやってみたことまとめ qiita.com

*1:ネイティブJS(onchange)ではなくReactのonChange属性!

*2:コレもJS(onclick)ではなくReactのonClick属性!