モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

工作HardwareHub ロゴ画像 (Laptop端末利用時)
工作HardwareHub ロゴ画像 (Mobile端末利用時)

React の基本的な使い方

モーダルを閉じる

ステッカーを選択してください

モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2017/11/05
最終更新最終更新
2023/02/05
記事区分記事区分
一般公開

目次

    フロントエンドエンジニア。React Hookが好きです!

    0
    ステッカーを贈るとは?

    React について、開発を行うために必要な基本事項について記載します。

    JSX 構文で React element を定義

    ReactDOM.render(element, container) の第一引数に指定する React element は JSX で記述できます。

    const element = <h1>Hello, world!</h1>;
    

    React element は関数から返すことができます。タグは入れ子にすることができます。{} で値を埋め込むこともできます。その場合、属性値はダブルクォーテーション等で囲わないようにします。

    function formatName(user) {
      return user.firstName + ' ' + user.lastName;
    }
    
    function getGreeting(user) {
      if (user) {
        return <h1 id={user.id}>Hello, {formatName(user)}!</h1>;
      }
      return <h1>Hello, Stranger.</h1>;
    }
    
    const user = {
      firstName: 'Harper',
      lastName: 'Perez',
      id: 'myid'
    };
    
    const element = (
      <div>
        {getGreeting(user)}
        <h2>Good to see you here.</h2>
      </div>
    );
    
    ReactDOM.render(
      element,
      document.getElementById('root')
    );
    

    以下のような仮想 DOM が生成されます。

    <div id="root">
      <div>
        <h1 id="myid">Hello, Harper Perez!</h1>
        <h2>Good to see you here.</h2>
      </div>
    </div>
    

    const で定義していることからも分かるとおり React element はイミュータブルです。再描画する場合は新規 React element を構築して ReactDOM.render() に再度渡します。ReactDOM.render() は変更のあった仮想 DOM だけを更新します。このことを、以下のコードでは setInterval を利用して確認しています。

    function tick() {
      const element = (
        <div>
          <h1>Hello, world!</h1>
          <h2>It is {new Date().toLocaleTimeString()}.</h2>
        </div>
      );
      ReactDOM.render(
        element,
        document.getElementById('root')
      );
    }
    setInterval(tick, 1000);
    

    React component から React element を返す

    React element を返すものは React component とよばれます。React component は function として記述することもできますが、実際には後述の state などが利用できる class として記述されます。

    function として定義された React component

    function App(props) {
      return <h1>Hello, {props.name}</h1>;
    }
    
    ReactDOM.render(
      <App name="myname" />,
      document.getElementById('root')
    );
    

    class として定義された React component

    class App extends React.Component {
      render() {
        return <h1>Hello, {this.props.name}</h1>;
      }
    }
    
    ReactDOM.render(
      <App name="myname" />,
      document.getElementById('root')
    );
    

    慣習として、アプリケーション全体を React で記述するときは App という名称のコンポーネントを root に render します。Rails テンプレートエンジン内で利用する場合などは複数箇所で render するためその限りではありません。

    状態をもつ React component

    class で記述した React component は、状態 statecomponentDidMount, componentWillUnmount といったライフサイクルフック持つことができます

    class Clock extends React.Component {
    
      constructor(props) {
        super(props);
        // コンストラクタ内では setState を
        // 利用せず state に直接代入します。
        this.state = {
          date: new Date(),
          cnt: 0
        };
      }
    
      // render 後に実行されるライフサイクルフックです。
      componentDidMount() {
        this.timerID = setInterval(
          () => this.tick(),
          1000
        );
        // 以下のようにしても同じ意味です。
        // const self = this;
        // this.timerID = setInterval(
        //   function(){ self.tick() },
        //   1000
        // );
      }
    
      // 仮想 DOM から本 component が削除される前に実行される
      // ライフサイクルフックです。
      componentWillUnmount() {
        clearInterval(this.timerID);
      }
    
      tick() {
        // コンストラクタ外では setState を利用します。
        this.setState({
          date: new Date()
        });
    
        // setState は上記のオブジェクトを引数に指定するものと
        // 下記の関数を引数に指定するものがあります。
        this.setState((prevState, props) => {
          return {
            cnt: prevState.cnt + parseInt(props.incr)
          };
        });
    
        // 以下のようにしても同じ結果です。
        // this.setState(prevState => {
        //   return {
        //     cnt: prevState.cnt + 5
        //   };
        // });
    
        // 以下のようにしても同じ意味です。
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
        // this.setState((prevState, props) => ({
        //   cnt: prevState.cnt + parseInt(props.incr)
        // }));
        // this.setState(function(prevState, props){
        //   return {
        //     cnt: prevState.cnt + parseInt(props.incr)
        //   };
        // });
      }
    
      // React element を返します。
      render() {
        return (
          <div>
            <p>time: {this.state.date.toLocaleTimeString()}</p>
            <p>cnt: {this.state.cnt}</p>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <Clock incr="5" />,
      document.getElementById('root')
    );
    

    イベントハンドリング

    React におけるイベントハンドラは以下のように記述します。SyntheticEvent に記載のあるイベントをハンドリングできます。

    class MyComponent extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {isToggleOn: true};
    
        // `handleClick` 内で `this` を利用するために忘れずに記述します。
        this.handleClick = this.handleClick.bind(this);
      }
    
      // `this` を利用するイベントハンドラ
      handleClick(e) {
        e.preventDefault(); // <a> タグの画面遷移を無効化したい場合
        this.setState(prevState => ({
          isToggleOn: !prevState.isToggleOn
        }));
      }
    
      // 引数のあるイベントハンドラ
      handleClickWithArgs(myarg, e) {
        console.log(myarg);
      }
    
      render() {
        return (
          <div>
            <a href="#" onClick={this.handleClick}>
              {this.state.isToggleOn ? 'ON' : 'OFF'}
            </a>
            <button onMouseOver={this.handleClickWithArgs.bind(this, 123)}>マウスオーバーでログ出力</button>
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('root')
    );
    

    条件分岐

    if 文による分岐以外に、以下のような記法が利用できます

    ReactDOM.render(
      <div>
        <h1>hello</h1>
        {true && <p>表示される</p>}
        {false && <p>表示されない</p>}
        {true ? <p>表示される</p> : <p>表示されない</p>}
      </div>,
      document.getElementById('root')
    );
    

    React component が React element として null を返すと何も表示されません。

    function App(props) {
      if(!props.show) {
        return null;
      }
      return <h1>hello</h1>;
    }
    
    ReactDOM.render(
      <App show={false} />,
      document.getElementById('root')
    );
    

    複数の React elements を返す React component

    React component は複数の React elements を返すことができますmap を利用すると便利です。

    function ListItem(props) {
      // `map` 内ではないため `key` を指定する必要はありません。
      return <li>{props.value}</li>;
    }
    
    function NumberList(props) {
      const numbers = props.numbers;
      const listItems = numbers.map(number =>
        <ListItem
          key={number.toString()} // `map` 内で生成する要素には何らかの `key` を指定します。
          value={number} />  // `key` は props として渡されません。必要に応じて別途指定します。
      );
      const listItems2 = numbers.map((number, index) => // `index` も利用できます (非推奨)
        <ListItem
          key={index} // `key` は同じリスト `listItems2` 内でユニークであれば十分です。
          value={number} />
      );
      return (
        <div>
          <ul>{listItems}</ul>
          <ul>{listItems2}</ul>
        </div>
      );
    }
    
    const numbers = [1, 2, 3, 4, 5];
    ReactDOM.render(
      <NumberList numbers={numbers} />,
      document.getElementById('root')
    );
    

    React component の階層化

    様々な component の共通部分を汎用的な component として切り出して、それを複数 component の内部で利用することでコード全体のメンテナンス性が上がります。このような階層化された component 群に関するいくつかの有益なサンプルを示します。

    複数 component で状態を共有

    上位 component の state を下位 component に渡すことで、component 群が全体として持つ状態を統一できます。上位 component は下位 component にコールバック関数を合わせて渡しておき、下位 component 内で状態を更新する必要が発生した場合はそれを呼び出して上位 component の state を更新します。

    class MyInnerComponent extends React.Component {
    
      constructor(props) {
        super(props);
        // 前述のとおり `handleChange` 内で `this` を利用するために忘れずに記述します。
        this.handleChange = this.handleChange.bind(this);
      }
    
      handleChange(e) {
        this.props.onMyChange(e.target.value); // input の値を取得してコールバック関数を実行します。
      }
    
      render() {
        return <input value={this.props.myval} onChange={this.handleChange} />;
      }
    }
    
    class MyComponent extends React.Component {
    
      constructor(props) {
        super(props);
        this.handleChange1 = this.handleChange1.bind(this); // 前述のとおり `handleChange1/2` 内で
        this.handleChange2 = this.handleChange2.bind(this); // `this` を利用するために忘れずに記述します。
        this.state = {myval: 0.0};
      }
    
      handleChange1(v) {
        const myval = parseFloat(v); // そのまま格納
        this.setState({myval: Number.isNaN(myval) ? 0.0 : (myval / 1.0)});
      }
    
      handleChange2(v) {
        const myval = parseFloat(v); // 半分にして格納
        this.setState({myval: Number.isNaN(myval) ? 0.0 : (myval / 2.0)});
      }
    
      render() {
        const myval = this.state.myval; // そのまま表示、二倍にして表示
        return (
          <div>
            <MyInnerComponent myval={myval * 1.0} onMyChange={this.handleChange1} />
            <MyInnerComponent myval={myval * 2.0} onMyChange={this.handleChange2} />
          </div>
        );
      }
    }
    
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('root')
    );
    

    React elements を props として渡す

    props.children を利用すると以下のような React element の渡し方ができます。

    function MyInnerComponent(props) {
      return (
        <div>
          {props.children}
        </div>
      );
    }
    
    function MyComponent() {
      return (
        <MyInnerComponent>
          <h1>hello</h1>
        </MyInnerComponent>
      );
    }
    
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('root')
    );
    

    複数の React element を渡す必要がある場合は、通常どおり属性値として渡します。

    function MyInnerComponent(props) {
      return (
        <p>
          {props.p1}, {props.p2}
        </p>
      );
    }
    
    function MyComponent() {
      return (
        <MyInnerComponent
          p1={
            <span>Hello</span>
          }
          p2={
            <span>world!</span>
          }
        />
      );
    }
    
    ReactDOM.render(
      <MyComponent />,
      document.getElementById('root')
    );
    

    form の制御

    React を使用しない場合であっても、一般に form タグ内の input 要素は状態を持ちます。これを React state と一致させることで form の状態を React から制御できます

    input タグ、textarea タグ、select タグ

    以下の例において、入力文字列が変化すると、大文字化されて state に格納されて、即時で input 要素の value が更新されます。結果として、小文字が入力できない form を実現できます。注意すべき箇所は以下のとおりです。

    • JSX において <textarea>...</textarea><textarea value={...} /> と記載される
    • JSX において <option selected ...<select value={} ... と記載される

    input/textarea/select すべてを類似の記法で扱えることが分かります。

    class MyForm extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {value: ''}; // input の初期値です。
        // `handleChange/Submit` 内で `this` を利用するために忘れずに記述します。
        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }
    
      handleChange(event) {
        this.setState({value: event.target.value.toUpperCase()}); // 大文字化してみます。
      }
    
      handleSubmit(event) {
        alert('submitted value: ' + this.state.value);
        event.preventDefault();
      }
    
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <input value={this.state.value} onChange={this.handleChange} />
            <textarea value={this.state.value} onChange={this.handleChange} />
    
            <select value={this.state.value} onChange={this.handleChange}>
              <option value="AAA">AAA</option>
              <option value="BBB">BBB</option>
            </select>
    
            <input type="submit" />
          </form>
        );
      }
    }
    
    ReactDOM.render(
      <MyForm />,
      document.getElementById('root')
    );
    

    チェックボックス、ラジオボタン

    input 要素毎に異なる state を持たせる場合でイベントハンドラを共用にする場合、event.targettypename で更新すべき state を識別します。以下の例ではチェックボックスとラジオボタンがそれぞれ別々の state を持っており、どちらも handleChange をイベントハンドラとして利用しています。

    class MyForm extends React.Component {
    
      constructor(props) {
        super(props);
        this.state = {
          checkboxValue: true,
          radioValue: 'aaa'
        };
        // `handleChange` 内で `this` を利用するために忘れずに記述します。
        this.handleChange = this.handleChange.bind(this);
      }
    
      handleChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name;
    
        console.log(this.state);
    
        // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Object_initializer#Computed_property_names
        this.setState({
          [name]: value
        });
      }
    
      render() {
        return (
          <form>
            <input
              name="checkboxValue"
              type="checkbox"
              checked={this.state.checkboxValue}
              onChange={this.handleChange} />
            <input
              name="radioValue"
              type="radio"
              value="aaa"
              checked={this.state.radioValue === 'aaa'}
              onChange={this.handleChange} />
            <input
              name="radioValue"
              type="radio"
              value="bbb"
              checked={this.state.radioValue === 'bbb'}
              onChange={this.handleChange} />
          </form>
        );
      }
    }
    
    ReactDOM.render(
      <MyForm />,
      document.getElementById('root')
    );
    

    よく利用する npm パッケージ

    0
    詳細設定を開く/閉じる
    アカウント プロフィール画像 (本文下)

    フロントエンドエンジニア。React Hookが好きです!

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    さらに詳しく →
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      関連記事