React

はじめてのReact #22「ToDoアプリを作る」 後編

3回に渡ってお送りしたToDoアプリ開発も最終回。今回はAjaxを利用しデータをサーバに保存してみます。

Propsのデータ型をチェック

以前取り上げたPropsのデータ型のチェックを導入しておきます。

プロジェクトも運用段階に入りComponentがどこからどのように呼び出されるか、収集がつかなくなったとしても呼び出し方さえ守ってもらえれば何とかなったりするものです(それが良いかはさて置き)。また誤った利用方法をするとテスト段階でエラーを明示的に出してくれるのもありがたいものです。

src/header.js

import PropTypes from 'prop-types';

// <Header>
//   React ToDo
// </Header>
Header.propTypes = {
  children: PropTypes.string.isRequired
};

src/todocreate.js

import PropTypes from 'prop-types';

// <ToDoCreate onClick={this.handleClick} />
ToDoCreate.propTypes = {
  onClick: PropTypes.func.isRequired
};

src/todolist.js

import PropTypes from 'prop-types';

// <ToDoList data={this.state.todo} remove={this.handleRemove}/>
ToDoList.propTypes = {
  data: PropTypes.array.isRequired,
  remove: PropTypes.func.isRequired
};

src/todoitem.js

import PropTypes from 'prop-types';

// <ToDoItem key={i.id} item={i} remove={this.props.remove} />
ToDoItem.propTypes ={
  item: PropTypes.object.isRequired,
  remove: PropTypes.func.isRequired
};

データをサーバ側で管理する

APIサーバを用意

Reactから話がずれますが、学習用に保存や取り出しを行うAPIサーバをNode.jsで準備します。かなり簡易的な物なので本番投入はしないでください。しないとは思いますがw

まずは適当な名前のディレクトリを作成しカレントディレクトリを別の場所に移します。今のプロジェクトは全く別の場所に作成してください。その後簡単にHTTPサーバを作成できるexpressを入れます。

$ mkdir apiserve; cd apiserve
$ npm install express

詳細は説明しませんが、以下のスクリプトをexpressをインストールしたディレクトリに適当な名前で保存します。ここではserve.jsとしました。

続きを読む

はじめてのReact #21「ToDoアプリを作る」 中編

前回に引き続き今回もToDoアプリを作っていきます。目標としてはフォームの使い勝手を向上させつつToDoの削除に対応するところまでとなります。

フォームを改良する

Submitイベントに対応

現状、登録フォームのテキストボックスに内容を入力しエンターキーを押した瞬間、再読込されてしまいます。これを防ぐには、formタグにonSubmitイベントを定義してあげればOK。今回はエンターキーを押すと確認用のダイアログを表示し、OKボタンが押されれば追加処理を行う形に変更しました。

src/todocreate.jsを以下のように変更します。

import React, { Component } from 'react';

class ToDoCreate extends Component {
  constructor(props){
    super(props);
    this.state = {
      newtodo: ""
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(e){
    this.setState({
      newtodo: e.target.value
    });
  }

  handleSubmit(e){
    let newtodo = this.state.newtodo;
    if(newtodo !== "" && window.confirm('本当に登録しますか?')){
      this.props.onClick(newtodo);
      this.setState({newtodo:""});
    }
    e.preventDefault();
  }

  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        <input type="text" value={this.state.newtodo} onChange={this.handleChange}/>
        <button>追加</button>
      </form>
    );
  }
}

export default ToDoCreate;

注意すべき点としてはconfirm()ではなく、window.confirm()とする必要があるところでしょうか。

Validationを行う

もう一つの問題点として、テキストボックスが空でも登録されている点ですね。これも直して置きます。src/todocreate.jsのhandleSubmit()を以下のように変更しました。空文字列または2文字未満であれば何もしません。

  handleSubmit(e){
    e.preventDefault();

    let newtodo = this.state.newtodo;
    if(newtodo === "" || newtodo.length < 2){
      return(false);
    }

    if(newtodo !== "" && window.confirm('本当に登録しますか?')){
      this.props.onClick(newtodo);
      this.setState({newtodo:""});
    }
  }

テキストボックスにfocusする

ToDoを入力する際にわざわざテキストボックスをクリックするのも面倒なので、ページが読み込まれたと同時にfocusをしてあげます。src/todocreate.jsを以下のように変更します。

import ReactDOM from 'react-dom';  //追加

class ToDoCreate extends Component {
  // (snip)

  componentDidMount(){
    ReactDOM.findDOMNode(this.refs.newtodo).focus();
  }

  // (snip)

  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        <input type="text" ref="newtodo" value={this.state.newtodo} onChange={this.handleChange}/>
        <button>追加</button>
      </form>
    );
  }
}

上記は変更した部分だけを抜き出しています。

まずReactDOMの機能を利用しますので新たにimportしました。
componentDidMount()はLifeCycleの機能。ComponentがDOMツリーに追加された瞬間に呼び出されます。ここで単純にfocus()メソッドを実行してあげればよいのですが、問題はどうやってテキストボックスを特定するかです。

今回はテキストボックスに<input type="text" ref="newtodo">といった形でref属性を追加しました。この属性をつけておくとReactから簡単に参照することができるようになります。この場合だとthis.ref.newtodoですね。

ReactDOM.findDOMNode()は最終的にDOMオブジェクトを返却しますので、これでテキストボックスを検索し、戻り値のDOMオブジェクトに対してfocus()を実行しているといます。

Lifecycleについては過去の記事を参照してください。

続きを読む

はじめてのReact #20「ToDoアプリを作る」 前編

前回準備した環境を使って、早速Webアプリを作ってみたいと思います。こういうときはToDoアプリを作るのが伝統となっていますので、それに習いますw

なお、本来であればテストコードを書きながら進めるべきだとは思いますが、今回は割愛しております。

準備編

プロジェクトの作成

rtodoという名前でプロジェクトを新規作成しました。rはReactで作るぞという意思表示で特に意味はありませんw 好きな名称をつけてください。

$ create-react-app rtodo

Gitリポジトリの準備

GitHubにもrtodoという名前でリポジトリを準備しました。まずはこいつにgit pushします。
https://github.com/katsube/rtodo

$ git remote add origin git@github.com:katsube/rtodo.git
$ git push -u origin master

Componentを作ってみる

新しいComponentを作る

まずは腕鳴らしに簡単なComponentを用意してみます。src/header.jsというファイルを作成し、その中に以下のコードを記述します。render()でタグを描画しているだけですね。h1の中はprops.childrenでもらった文字列をそのまま出しています。

import React, { Component } from 'react';

class Header extends Component {
  render() {
    return(
      <header>
        <h1>{this.props.children}</h1>
      </header>
    );
  }
}

export default Header;

最後のexport文を忘れないように気をつけてください。これを記述することで他のファイルからこのComponentを利用することができるようになります。

子供の要素にアクセスする方法(this.props.children)については過去の記事を参照してください。

続きを読む

はじめてのReact #19「開発環境を準備する」create-react-app編

ここまでご紹介してきたサンプルは学習やちょっとした検証には使えるのですが、実際に本番環境で動かすのには向いていません。というわけで今回は本番環境で動かすコードを作成するための開発環境を準備してみたいと思います。

Reactの開発環境

Node.jsのインストール

まず最初にNode.jsを入れる必要があるのですが、アップデートに対応しやすくするためバージョン切り替えが手軽に行えるツールをインストールした上でNode.jsの最新版を入れます。

MacやLinuxならnodebrew

Windowsならnodist

今回は以下のバージョンで構築を開始します。npmはNodejs用のパッケージマネージャーで、これを使って様々な便利ライブラリやツールをインストールすることができます。

$ node --version
v11.7.0
$ npm --version
6.5.0

create-react-appのインストール

Reactの開発環境をゼロから作ろうとすると中々に面倒なのですが、コマンド一発で必要な物を一式揃えてくれる便利ツールがfacebookより公式に提供されているので今回はこちらを利用してみます。

では先ほどインストールしたnpmコマンドを利用してこのcreate-react-appを入れます。npmを利用するとコマンド一発で入ります。

$ npm install -g create-react-app

今回は以下のバージョンが入りました。

$ create-react-app --version
2.1.3

続きを読む

はじめてのReact #18「Ajaxでデータ取得」

昨今のWebアプリではWebAPI(RESTful API等)をAjaxなどで叩きデータを取得する、もしくはデータを保存するといった行為を日常的に行っているわけですが、今回はAjaxでデータ取得し表示するまでに挑戦してみます。

MonsterView Component

とあるRPGのモンスターの一覧が記録されたJSONファイルをAjaxで取得し、そのままReactで表示するサンプルです。公式ドキュメントの例をちょろっといじったものになります。

サンプル

データファイルです。実際にはサーバ側で動的に出力することが多いと思いますが、今回は静的なファイルで試します。ファイル名はdata.json

{
  "monsters": [
    { "id": 1, "name": "スライム", "hp": 10 },
    { "id": 2, "name": "ドラキー", "hp": 12 },
    { "id": 3, "name": "おおありくい", "hp": 36 }
  ] 
}

実際の処理を行うHTMLです。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>MonsterView</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
class MonsterView extends React.Component {
  constructor(props) {
    super(props);
    this.state ={
      isLoaded: false,
      error: null,
      monsters: []
    }
  }

  componentDidMount() {
    fetch("https://s3-us-west-2.amazonaws.com/blog.katsubemakito.net/static/react1st/18/data.json")
      .then(res => res.json())
      .then(
        (result) => {
          this.setState({
            isLoaded: true,
            monsters: result.monsters
          });
        },
        (error) => {
          this.setState({
            isLoaded: true,
            error: error
          });
        }
      )
  }

  render() {
    if (this.state.error) {
      return <div>Error: {this.state.error.message}</div>;
    }
    else if ( !this.state.isLoaded ) {
      return <div>Loading...</div>;
    }
    else {
      return (
        <ul>
          {this.state.monsters.map( monster => (
            <li key={monster.id}>
              {monster.name} {monster.hp}
            </li>
          ))}
        </ul>
      );
    }
  }
}

ReactDOM.render(
  <MonsterView />,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #17「CSSを適用する」

これまで何の前触れもなくチラッと出てきたComponentにCSSを適用する方法について、いよいよ解説したいと思います。まぁブログの構成(順番)を間違えてるだけなんですけどねw もっと早く取り上げるべきだったw

ダイアログ Component

Component名は前回と同じですが、今回はボタンをクリックする度に文字色が変化するサンプルにしてみました。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Dialog</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

  <style type="text/css">
    .dialog {
      border: 1px solid gray;
      background-color: beige;
      text-align: center;
    }
  </style>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
class Dialog extends React.Component {
  constructor(props) {
    super(props);
    this.state ={
      i: 0
    }
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(){
    this.setState({
      i: this.state.i + 1
    });
  }

  render() {
    let colormap = ["black", "red", "green", "blue"];
    let i = this.state.i % colormap.length;
    let color = {
      color: colormap[i]
    };

    return (
      <div className="dialog">
        <h1 style={color}>HelloWorld</h1>
        <button onClick={this.handleClick}>チェンジ</button>
      </div>
    );
  }
}

ReactDOM.render(
  <Dialog />,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #16「Componentの連携」子要素にアクセスする

これまでは、最終的にrender()する際には<Foo />のようにタグで何らかの文字列や要素を囲わない状態でした。今回はそこから一歩進んで<Foo>Hello!</Foo>のように何らかの要素を挟んでみたいと思います。

ダイアログ Component

おもしろサンプルが思いつかなかったので、公式ドキュメントほぼそのまま、ダイアログの作成を行います。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>Dialog</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
class Dialog extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    return (
      <div style={{backgroundColor:'lightpink', width:'500px', border:'5px solid red'}}>
        {this.props.children}
      </div>
    );
  }
}

ReactDOM.render(
  <Dialog>
    <h1>Hello! World</h1>
    nice to meet you!
  </Dialog>,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #15「Componentの連携」複数同時に処理をする

いよいよReactの真骨頂っぽいところに足を突っ込んでみます。前回はComponentの中にComponentが1つだけ内包されていましたが、これを複数にしてみたいと思います。

西暦・和暦 相互変換Component

今年の5月には新しい年号が爆誕するのにちなんで、西暦と和暦を相互に変換するComponentを作成してみたいと思います。今回も公式ドキュメントのサンプルを改変した物になります。

実行すると昭和、平成、西暦の3つのテキストボックスが表示されます。いずれか1つに入力すると残りの2つが連動して変化します。コードがちょっと長いのですが、理解のしやすさを優先しただけで実際にやってることは大したことありません。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>HeiseiConvert</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
class YearInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onYearChange(e.target.value);
  }

  render() {
    const year = this.props.year;
    const type = this.props.type;

    return (
      <fieldset>
        <legend>{type}:</legend>
        <input value={year}
               placeholder={this.props.placeholder}
               onChange={this.handleChange} /> 
               年
      </fieldset>
    );
  }
}

class HeiseiConvert extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
          ad: "",
       showa: "",
      heisei: ""
    };

    this.handleAdChange     = this.handleAdChange.bind(this);
    this.handleShowaChange  = this.handleShowaChange.bind(this);
    this.handleHeiseiChange = this.handleHeiseiChange.bind(this);
  }

  handleAdChange(year) {
    this.setState({
          ad: year,
       showa: this.toConvert(year, -1925),
      heisei: this.toConvert(year, -1989)
    });
  }

  handleShowaChange(year) {
    this.setState({
          ad: this.toConvert(year, 1925),
       showa: year,
      heisei: this.toConvert(year, -63)
    });
  }

  handleHeiseiChange(year) {
    this.setState({
          ad: this.toConvert(year, 1988),
       showa: this.toConvert(year, 63),
      heisei: year
    });
  }

  toConvert(year, value){
    if( Number.isNaN(parseFloat(year)) ){
      return('');
    }
    return( Number(year) + value);
  }

  render() {
    return (
      <div>
        <YearInput
          type="昭和"
          placeholder="例: 64"
          year={this.state.showa}
          onYearChange={this.handleShowaChange} />
        <YearInput
          type="平成"
          placeholder="例: 30"
          year={this.state.heisei}
          onYearChange={this.handleHeiseiChange} />
        <YearInput
          type="西暦"
          placeholder="例: 2019"
          year={this.state.ad}
          onYearChange={this.handleAdChange} />
      </div>
    );
  }
}

ReactDOM.render(
  <HeiseiConvert />,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #14「Componentの連携」入力された値を動的に渡す

今回はユーザーが入力した値を別のComponentにリアルタイムに渡すサンプルを作ってみたいと思います。

カラーチェッカーComponent

テキストボックスに色の名前を入力すると、ほぼリアルタイムに■の色が変化するComponentになります。Reactの公式ドキュメントの水の温度によって表示を変化させるサンプルを改変した物です。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>ColorChecker</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
function ShowColor(props) {
  return(
    <p style={{color:props.color, fontSize:'20px'}}>■</p>
  );
}

class ColorChecker extends React.Component {
  constructor(props) {
    super(props);
    this.state = {color: ''};

    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.setState({color: e.target.value});
  }

  render() {
    const color = this.state.color;
    return (
      <fieldset>
        <legend>色名を入力してください:</legend>
        <input
          value={color}
          onChange={this.handleChange}
          placeholder="例:Red" />

        <ShowColor color={color} />
      </fieldset>
    );
  }
}

ReactDOM.render(
  <ColorChecker />,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #13「フォームと連動する」ラジオボタン編

Reactでフォームを使ってみようシリーズも佳境に差し迫った第4弾。今回はラジオボタンを触ってみます。

時限爆弾の解体ラジオボタンComponent

もはら映画で定番となった、時限爆弾の解体シーンで「青と赤どちらのコードを切るか…」を再現するComponentを作ってみたいと思います。3色のラジオボタンが並んでおりハズレを選択(コードを切断)すると爆発します。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>BomButton</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<h1>BomButton</h1>
<p>線を一つ選んで爆弾を解除してください</p>
<img src="./bom1.png">
<div id="root"></div>

<script type="text/babel">
class BomButton extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      radio: [
        {cd:'red',   name:"赤", checked:false},
        {cd:'blue',  name:"青", checked:false},
        {cd:'green', name:"緑", checked:false}
      ],
      lastclick: null  //最後に選択された色を入れる
    };

    // State内の色の中から抽選を行う
    let i = Math.floor(Math.random() * 100) % this.state.radio.length;
    this.hit = this.state.radio[i].cd;

    // 爆発時の素材を事前ロードしておく
    this._preload();

    // イベント用メソッド
    this.handleRadioClick = this.handleRadioClick.bind(this);
  }

  /**
   * 爆発時の素材を事前ロード
   */
  _preload(){
    this.se = new Audio('./bomb1.mp3');
    this.se.load();

    let img = new Image();
    img.src = "./bakuhatsu.png";
  }

  /**
   * ラジオボタンをクリック
   */
  handleRadioClick(e) {
    if( ! confirm('本当にこの線で良いですか?') ){
      return(false);
    }

    let name  = e.target.name;
    let value = this.state.radio.map( (item)=>{
      return({
        cd: item.cd,
        name: item.name,
        checked: (item.cd===name)? true:false
      })
    });

    this.setState({
        radio: value
      , lastclick: name
    });
  }

  render() {
    //----------------
    // 当選したら爆発
    //----------------
    if( this.hit === this.state.lastclick ){
      this.se.play();
      return <div><img src="./bakuhatsu.png" /></div>
    }
    //----------------
    // 選択肢の表示
    //----------------
    else{
      const radio = this.state.radio;
      const listItems = radio.map((r) =>
        <label key={r.cd.toString()} style={{color: r.cd}}>
          <input
            type="radio"
            name={r.cd}
            value={r.cd}
            checked={r.checked}
            onChange={this.handleRadioClick} />
          {r.name}<br />
        </label>
      );
      return <form>{listItems}</form>
    }
  }
}

ReactDOM.render(
  <BomButton />,
  document.getElementById('root')
);
</script>
</body>
</html>

続きを読む

はじめてのReact #12「フォームと連動する」チェックボックス編

Reactでフォームを使ってみようシリーズも第3弾。今回のテーマはチェックボックスです。

都道府県チェックボックスComponent

よく見かける都道府県一覧のチェックボックスです。例えば「中国地方」をチェックすると、島根や鳥取など中国地方全体にチェックが入る機能もついてます。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>AreaCheck</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>

<div id="root"></div>

<script type="text/babel">
  class AreaCheck extends React.Component {
    constructor(props) {
      super(props);
      this.cd = this.props.cd;
      this.state = {
        area: this.props.area
      };

      this.handleKenChange = this.handleKenChange.bind(this);  //各都道府県のチェック
      this.handleAllChange = this.handleAllChange.bind(this);  //まとめてチェック
    }

    /**
     * 各都道府県をチェック
     */
    handleKenChange(e) {
      let name  = e.target.name;
      let value = this.state.area.map( (ken)=>{
        return({
          cd: ken.cd,
          name: ken.name,
          checked: (ken.cd===name)? e.target.checked:ken.checked
        })
      });

      this.setState({area: value});
    }

    /**
     * まとめてチェック
     */
    handleAllChange(e){
      let value = this.state.area.map( (ken)=>{
        return({
          cd: ken.cd,
          name: ken.name,
          checked: e.target.checked
        })
      });

      this.setState({area: value});
    }

    render() {
      let area = this.state.area;
      let listItems = area.map((ken) =>
        <label key={ken.cd.toString()}>
          <input
            type="checkbox"
            name={ken.cd}
            value={ken.cd}
            checked={ken.checked}
            onChange={this.handleKenChange} />
            {ken.name}
        </label>
      );

      return (
        <form>
          <label key={this.cd}>
            <input type="checkbox" name="ALL" onChange={this.handleAllChange} />
            このエリアをチェック
          </label>
          <br />
          {listItems}
        </form>
      );
    }
  }

  const Chugoku = [
    {cd:'31', name:'鳥取県', checked:false},
    {cd:'32', name:'島根県', checked:false},
    {cd:'33', name:'岡山県', checked:false},
    {cd:'34', name:'広島県', checked:false},
    {cd:'35', name:'山口県', checked:false}
  ];
  const Shikoku =[
    {cd:'36', name:'徳島県', checked:false},
    {cd:'37', name:'香川県', checked:false},
    {cd:'38', name:'愛媛県', checked:false},
    {cd:'39', name:'高知県', checked:false}
  ];
  ReactDOM.render(
    <div>
      <AreaCheck cd="CG" area={Chugoku} />
      <AreaCheck cd="SK" area={Shikoku} />
    </div>,
    document.getElementById('root')
  );
</script>
</body>
</html>

続きを読む

はじめてのReact #11「フォームと連動する」プルダウン編

前回のテキストボックスに続き、今回はプルダウン(<select>)の操作方法になります。
ほぼ同じやり方で<ul>, <ol>などのリストにも適用できます。

メンバーリストComponent

メンバーの一覧をプルダウンとして表示し、名前をクリックすると削除されるComponentです。公式ドキュメントの「Lists and Keys」のサンプルコードを参考にしています。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>MemberList</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
   class MemberList extends React.Component {
     constructor(props) {
       super(props);
       this.state = {
          guys: props.guys
       };

       this.handleRemove = this.handleRemove.bind(this);
     }

     handleRemove(id){
       const guys = this.state.guys;
       const new_guys = [];

         for( let i=0; i<guys.length; i++ ){
           if(guys[i].id !== id){
             new_guys.push(guys[i]);
           }
       }

       this.setState({guys:new_guys});
     }

     render(){
       const guys = this.state.guys;
       const listItems = guys.map((guy) =>
         <option key={guy.id.toString()} onClick={ ()=>{ this.handleRemove(guy.id) } }>
           {guy.name}
         </option>
       );

       return <select size="3">{listItems}</select>;
     }
   }


    const guys = [
      {id:10, name:"Foo"},
      {id:20, name:"Bar"},
      {id:30, name:"Hoge"}
    ];

    ReactDOM.render(
      <MemberList guys={guys} />,
      document.getElementById('root')
    );
  </script>
</body>
</html>

続きを読む

はじめてのReact #10「フォームと連動する」テキストボックス編

10回目ともなるとだいぶReactで何かを作ることにも慣れてきましたねw 今回はいよいよフォームと関連した処理を書いてみたいと思います。Reactに限らずですがユーザーの入力とリアルタイムに連動して表示が切り替わるのは作っていて楽しいですよね。

入力候補を表示してくれるComponent

ユーザーが入力した文字に応じて、入力候補を自動的に表示してくれるComponentを作成します。今回も公式ドキュメントにあるサンプルコードを、編集して作成した物です。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>AutoCompletionBox</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    class AutoCompletionBox extends React.Component {
      constructor(props) {
        super(props);
        this.INITIAL_WORD = '入力してください';

        this.state = {
          value: '',                 //ユーザーが入力した文字列が入る
          word: this.INITIAL_WORD    //自動補完用の文字列が入る
        };

        this.handleChange = this.handleChange.bind(this);
        this.handleSubmit = this.handleSubmit.bind(this);
      }

      handleChange(e) {
        let buff = e.target.value;            //入力された文字列
        let ac   = this.candidateData(buff);  //自動補完された文字列

        this.setState({value: buff, word: ac});
      }
      handleSubmit(e) {
        // 初期文言と自動補完用の文字が異なっていればStateを更新
        if( this.state.word !== this.INITIAL_WORD ){
          this.setState({value: this.state.word});
        }
        e.preventDefault();  //Submitイベントによる画面遷移をキャンセル
      }

      /**
       * 自動補完用の文字列を返却
       */
      candidateData(key){
        let i = key.slice(0,1).toLowerCase();  //先頭の1文字を取り出し小文字に
        let data = {
          a: 'Android',
          b: 'BlackBerryOS',
          f: 'FireFoxOS',
          i: 'iOS',
          t: 'Tizen',
          u: 'Ubuntu Touch',
          w: 'Windows 10 Mobile'
        };

        return( ( i in data )?  data[i]:this.INITIAL_WORD );
      }

      render() {
        return (
          <form onSubmit={this.handleSubmit}>
            <label>
              お使いのスマートフォンのOSは?<br />
              <input type="text" value={this.state.value} onChange={this.handleChange} /><br />
              <input type="submit" value={this.state.word} />
            </label>
          </form>
        );
      }
    }

    ReactDOM.render(
      <AutoCompletionBox />,
      document.getElementById('root')
    );
  </script>
</body>
</html>

続きを読む

はじめてのReact #9「状態に合わせてComponentを切り替える」

Componentの状態をthis.setState()で変化させる度にrender()が自動的に実行され最新の描画がされるのですが、Reactでは単純なHTMLだけではなくComponent自体を別の物に切り替えることができます。

今回はReactの公式ドキュメント「Conditional Rendering」を単純化したサンプルコードでお送りしします。

ログインComponent

よくあるログインとログアウトの管理用のComponentをイメージしたサンプルになっています。最初はログインしていない状態ですが、ボタンをクリックすることでログイン状態になります。再度ボタンをクリックすると最初の状態(ログアウト)に戻るというシンプルなものです。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>LoginControl</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    class LoginControl extends React.Component {
      constructor(props) {
        super(props);

        this.handleLoginClick  = this.handleLoginClick.bind(this);
        this.handleLogoutClick = this.handleLogoutClick.bind(this);

        this.state = {
          isLoggedIn: false
        };
      }

      handleLoginClick() {
        this.setState({isLoggedIn: true});
      }
      handleLogoutClick() {
        this.setState({isLoggedIn: false});
      }

      render() {
        const isLoggedIn = this.state.isLoggedIn;
        let message;
        let button;

        if (isLoggedIn) {
          message = "ナイス、ログイン";
          button  = <LogoutButton onClick={this.handleLogoutClick} />;
        }
        else {
          message = "ログインしてください";
          button  = <LoginButton onClick={this.handleLoginClick} />;
        }

        return (
          <div>
            <h1>{message}</h1>
            {button}
          </div>
        );
      }
    }

    function LogoutButton(props) {
      return <button onClick={props.onClick}>ログアウト</button>;
    }

    function LoginButton(props) {
      return <button onClick={props.onClick}>ログイン</button>;
    }

    ReactDOM.render(
      <LoginControl />,
      document.getElementById('root')
    );
  </script>
</body>
</html>

続きを読む

はじめてのReact #8「イベント処理を試してみよう」

イベント処理は、タグ内にonClickなどのイベントハンドラ用の属性を指定することで定義できます。

これだけ聞くと正直気持ち悪いですよねw 結論から言うと最終的には問題ない形でレンダリングされます。実際のサンプルコードを元に話を進めていきましょう。

おみくじComponent

今回はボタンをクリックすると、運勢がアラートされる簡単な「おみくじ」を作成してみたいと思います。

サンプル

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8" />
  <title>おみくじ</title>
  <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

  <!-- Don't use this in production: -->
  <script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>

  <script type="text/babel">
    class Omikuji extends React.Component {
      dropOmikuji(e){
        let unsei = ["大吉", "吉", "中吉", "小吉", "凶", "大凶"];
        let i = Math.ceil(Math.random() * 100) % unsei.length;

        alert(`あなたの運勢は${unsei[i]}です`);  // ``内に ${} で変数を埋め込めるのはES2015からの追加仕様
      }

      render() {
        return <button onClick={this.dropOmikuji}>おみくじを引く</button>;
      }
    }

    ReactDOM.render(
      <Omikuji />,
      document.getElementById('root')
    );
  </script>
</body>
</html>

続きを読む