はじめてのReact

はじめてのReact #28 「CSSフレームワークを導入する」Material-UI グリッド編

前回はMaterial-UIのインストールと基本的な利用方法について取り上げました。今回は画面のレイアウトを行う際に利用するグリッドシステムです。

この手のCSSフレームワークの醍醐味の一つはグリッドが用意されていることですね。最近はflexboxが使えるようになってずいぶん楽になりましたが、細かい制御をやってくれるので中々重宝するんですよね。
続きを読む

はじめてのReact #27 「CSSフレームワークを導入する」Material-UI インストール編

Reactでは通常のWebサイトと同様、自由自在にCSSを記述し反映することができるわけですが、ゼロから書き始めるのは個人的に正直ダルい。そんなときにタグにクラス名を適当に指定すればチャチャッといい感じに表示してくれるCSSフレームワークを導入したいと思います。

今回はよく名前を見かけるMaterial-UIを使ってみることにします。長いものには巻かれろ精神。

続きを読む

はじめてのReact #26 「ルーティングに対応する」設定をJSONにまとめる編 react-router@v5

これまでルーティング関連の記事では以下のような内容を取り上げてきました。

  • 第23回 基本的なReactRouterの使い方
  • 第24回 URLの一部をパラメーターとして受け取る
  • 第25回 認証とリダイレクト

今までは以下のように直接<Route>を書いてルーティングの定義を行ってきましたが、今回はこれらの設定をJSONなどで定義し、いつでも外部のファイルに出せるようにします。

<!-- befor -->
<Switch>
  <Route exact path="/" component={Home} />
  <Route exact path="/auth" component={Auth} />
  <Route path="/area/:cd" component={Meisan} />
</Switch>

このままでも動くんですけどね。ロジックと設定は分離して置いた方がメンテしやすくなりますからね。

続きを読む

はじめてのReact #25 「ルーティングに対応する」認証とリダイレクト編 react-router@v5

前々回では基本的なReactRouterの使い方を、前回でURLの一部をパラメーターとして受け取ってみました。

今回はユーザーの状態に合わせてページの出し分けを行います。
具体的には

  • 認証済みのユーザーにだけページを表示
  • 未認証のユーザーにはログインページを表示

という仕様になります。
続きを読む

はじめてのReact #24 「ルーティングに対応する」URLパラメーター編 react-router@v5

前回はReact Routerの基本的な機能を利用し簡単な図鑑アプリを作成しました。

今回はリクエストを受けたURLの一部をパラメーターとして受け取ってみます。
例えばブログやTwitterのようなアプリで特定のIDの記事を表示したい場合/posts/view/123といった形式のURLにアクセスするとします。ここで問題になるのは1000個の記事があった場合、1000個の<Route>を定義する必要が出てくるのかという点ですね。こういった場合に特定のパターンのURLは一部の文字列をパラメーターとして設定することができます。

続きを読む

はじめてのReact #23 「ルーティングに対応する」基本編 react-router@v5

React Routerは雑に言うとリクエストされたURLのパスと、ReactのComponentを紐付けてくれる便利なモジュールです。

例えば /foo にアクセスされたら FooComponentを、/barにアクセスされたらBarComponentを実行することができるようになります。

今回は3月21日に登場したばかりのReact Router v5を触ってみます。本来はv4.4として出す予定だったそうですがReact 16との互換性が大きく改善されたとのことでメジャーバージョンアップの運びとなったとのこと。使い方もv4系とあまり変わらないようですね。

続きを読む

はじめての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>

続きを読む