reactjs
React

React初心者のまとめ(基本編:1/3)

React初心者の私がふとReactを使ってみようと思い立ち、社内ツールを作る過程で覚えたことをまとめておきます。
今まではJavaScriptはそこそこ経験してきたものの、もっぱらjQueryに頼って開発してきた旧型タイプの開発者ですので、同じような立場の方には参考にして頂けるのではないかなと思います。

Reactのイメージ(学習前)

そもそもReactが何なのかもよく分かっていなかった状態で始めたので、学習前に思っていた印象は…

  • Facebookが作ったものなので良さそう
  • jQueryを使わずにJavaScriptを使ったページを作れる(jQueryで追記するプログラム部分が複雑になることを回避できる?)
  • 動きが速そう
  • 覚えることは多そう
  • ソースはスッキリ書ける?

というかなり適当な印象と知識でスタートしました。

Reactの基本ルール

学習したうえで分かった「本当に基本のルール」はこちら。当たり前のこともいろいろあるのですが、初期の概念すらよく分かっていない状態であればこれをおさえておくことは大事なので記しておきます。これらの概念がHTML+jQueryでの書き方と大幅に異なるため難しく感じてしまうのですが、逆に言えばこの辺りを押さえられれば理解が早くなるはずです。

  • Reactでのレンダリングを指定したエリア内はすべてReactで生成する(=一部だけ生のHTMLで書くことはできない)
  • Reactはパーツ設計。Componentといわれる「パーツ」を組み合わせて構築する
  • Reactは値(=State)をもとにDOMをつくる
  • Stateは「自身のState」と「上位から引き継いだState(=props)」が使える
  • React内でのHTMLのところはJSXという書き方で書く
  • Reactが動作して書き出すHTMLソースは見えないことが多い。また意図しない形になることも多い(例えるならば、jQueryUIのライブラリが書き出すHTMLがどのようなものになるか把握できないことに近い)
  • React自身ではデザイン(CSS)は設計しない

…といわれてもよく分からないと思うので、実際のソースで順次説明します。

開発環境

とその前に、Reactを扱う上で今までと大幅に違うのがReactのソースはコンパイルする必要があることです。そのためには「開発環境」を構築する必要があります。
書いたjQueryをそのままFTPアップロードして動かしていた私のような旧型の開発者には、そもそもここの敷居が異常に高い。
ただそれはReactを作ったfacebook社も認識しているようで、イマドキは「Create React App」なるものがあり、環境を一気に構築できるそうです。
参考:Create React AppでReactアプリを開発する

ただしこれを使うにも、前提としてnode.jsのインストールとコマンド操作の最低ラインの知識は必須です。。
そこまではなんとか、がんばって学んでみてください。

なお環境構築は飛ばしてとりあえず学んでみたいという方は、ブラウザ上で動作チェックができる素晴らしいツールがありますので、下記を利用してみてください。これかなり使えるツールでオススメです。
CodeSandbox

基本中の基本のソース

開発環境が構築できたという前提のもと、早速ソースコードです。
下記は簡単なテキストを表示するだけのソースです。
なおsample1.jsxはJSXのため、webpack等のビルドツールでコンパイルして必要ファイルをパッケージしたものをsample1.jsとしてindex.htmlで読み込ませています。
(webpack等ビルドツールの説明はここでは省略します。)

hello world

sample1.jsx
import React from 'react';
import ReactDOM from 'react-dom';

/* コンポーネント */
class MyComponent extends React.Component {
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページはテストページです。</p>
            </div>
        )
    }
}

ReactDOM.render(
  <MyComponent />,
  document.getElementById('main') /* Reactが生成したコードを#mainに書き出す */
);
index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>Sample</title>
</head>
<body>
<div id="main">
<!--ここにReactで生成したソースがレンダリングされる-->
</div>
<script src="/js/sample1.js"></script><!--sample1.jsxをコンパイル+パッケージしたもの-->
</body>
</html>

<上記プログラムのポイント>

  • 冒頭2行のimportはReactを使うために書く決まり文句(jQueryを使うためにjquery.jsを読み込むイメージ)
  • React.Componentを継承(extends)してコンポーネントといわれる「パーツ」を作る。extends React.Componentは決まり文句。
  • タグ名=コンポーネントクラス名とすることでコンポーネントを設置できる
  • render()で生成したいHTML(正確にはjsx)を書く
  • render(){ return( の直下は1つのdomしか置けない
NG.
render(){
    return (
        <h1>Hello! World.</h1>
        <p>このページはテストページです。</p>
    )
}
OK.
render(){
    return (
        <div>
            <h1>Hello! World.</h1>
            <p>このページはテストページです。</p>
        </div>
    )
}

変数を使ってみる

{変数名}で変数を出力できます。

sample2.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {
    render(){
        let viewText = "テストページ";
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{viewText}です。</p>
            </div>
        )
    }
}

(省略)

(応用1)メソッドで書き出し

出力内容をメソッドで関数化して書き出すこともできます。

sample3-2.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {

    // テキスト出力するメソッド
    renderText(){
        return "テストページ";
    }
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{this.renderText()}です。</p>
            </div>
        )
    }
}

(省略)

(応用2)メソッドでDOMをレンダリング

DOM自体をメソッドで関数化して書き出すこともできます。

sample3-2.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {

    // DOMを出力するメソッド
    renderTextDom(){
        return(
            <p>このページはテストページです。</p>
        )
    }
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                {this.renderTextDom()}
            </div>
        )
    }
}

(省略)

Stateを使う

ReactはStateによって表示を変化させることがメイン機能です。まず機能を理解するため、sample2.jsxの関数をstateに置き換えただけのものを示します。

sample4.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = { viewText : 'テストページ' }
    }
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{ this.state.viewText }です。</p>
            </div>
        )
    }
}

(省略)
  • constructorを記述して、その中にthis.stateを記述してstateの初期値を設定
  • state内のプロパティ名(ここではviewText)と値はconstructor内で自由に決められる

書き方ルールが分かったところで、stateの値によって表示が変わる例を示します。

sample5.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = {
            viewText : 'テストページ',
            viewFlg  : 1, // 0または1を入れる
        }
    }
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{this.state.viewFlg == 1 ? this.state.viewText : 'サンプルページ'}です。</p>
            </div>
        )
    }
}

(省略)
  • viewFlgが1のとき:「このページはテストページです。」を表示
  • viewFlgが0のとき:「このページはサンプルページです。」を表示

stateを動的に変更

ボタンをクリックすることで表示を動的に切り替えてみます。

sample6.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = {
            viewText : 'テストページ',
            viewFlg  : 1,
        }
    }
    renderChange(){
        // viewFlgの状態を切替(0 <-> 1)
        let newFlg = this.state.viewFlg == 1 ? 0 : 1;
        this.setState({viewFlg : newFlg });
    }
    render(){
        return (
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{this.state.viewFlg == 1 ? this.state.viewText : 'サンプルページ'}です。</p>
                <button onClick={()=>this.renderChange()}>切替</button>
            </div>
        )
    }
}

(省略)

onClickでクリックした時の挙動を指定します。なおonClickはJSXでの記述方法であって、実際に生成されるソース上にはonClickは表示されません。あくまでReactに動きを伝えるための記述方法です。
またsetState関数を使って値を変化させることで、Reactが自動的にDOMを再レンダリングします。この例では
ボタンをクリックする(ユーザートリガー) => setStateで値を変更(Reactのレンダリングトリガー) => 再レンダリング
という流れになっています。

sample7.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class MyComponent extends React.Component {
    constructor(props,context){
        super(props,context)
        this.state = {
            viewText : props.text,
            viewFlg  : props.defaultFlg,
        }
    }
    renderChange(){
        // viewFlgの状態を切替(0 <-> 1)
        let newFlg = this.state.viewFlg == 1 ? 0 : 1;
        this.setState({viewFlg : newFlg });
    }
    render(){
        return(
            <div>
                <h1>Hello! World.</h1>
                <p>このページは{this.state.viewFlg == 1 ? this.state.viewText : 'サンプルページ'}です。</p>
                <button onClick={()=>this.renderChange()}>切替</button>
            </div>
        )
    }
}

ReactDOM.render(
  <MyComponent text={'テストページ'} defaultFlg={0} />,
  document.getElementById('main')
);

のように、属性のかたちで引き継ぎたい値を渡すことで、子コンポーネント内部でpropsのプロパティとして値を受け取ることができます。

(補足)constructorについて

stateを初期化する段階から登場しているconstructor()ですが、DOM挿入前の初期化時に呼ばれるメソッドです。
super(props,context)は当面は決まり文句の記述だと思ってください。
なおstateを使わずpropsだけのコンポーネントの場合など、内容がsuper(props,context)だけの場合にはconstructorの記述自体を丸々省略できます。

class MyComponent extends React.Component {

    constructor(props,context){
        super(props,context)
    }

    render(){
        <div>
            <p>{this.props.text}</p>
        </div>
    }
}

 ↓ constructorは不要(機能は同じ) ↓

class MyComponent extends React.Component {

    render(){
        <div>
            <p>{this.props.text}</p>
        </div>
    }
}

propsを使う

値はStateだけでなく、上位から値を引き継いで使用することもできます。これによってコンポーネントの使い回しがしやすくなります。

複数のコンポーネントを組み合わせる

コンポーネントの内部に別のコンポーネントを持つことができます。
sample7.jsxをベースに、一部DOMをコンポーネントとして分割して複数コンポーネントの形にしてみます。

sample7.jsx
import React from 'react';
import ReactDOM from 'react-dom';

class LeadText extends React.Component { 
  render() {
    return (
      <p>このページは{this.props.viewFlg == 1 ? this.props.viewText : 'サンプルページ'}です。</p>
    )
  }
}

// 親コンポーネント
class MyComponent extends React.Component {
  constructor(props, context) {
    super(props, context)
    this.state = {
      viewText: props.text,
      viewFlg: props.defaultFlg,
    }
  }
  renderChange() {
    // viewFlgの状態を切替(0 <-> 1)
    let newFlg = this.state.viewFlg == 1 ? 0 : 1;
    this.setState({ viewFlg: newFlg });
  }
  render() {
    return (
      <div>
        <h1>Hello! World.</h1>
        <LeadText viewText={this.state.viewText} viewFlg={this.state.viewFlg} />
        <button onClick={()=>this.renderChange()}>切替</button>
      </div>
    )
  }
}

ReactDOM.render(
  <MyComponent text={'テストページ'} defaultFlg={0} />,
  document.getElementById('main')
);

今回は以上です。Reactの基本的な動きが理解いただけましたでしょうか。

  • コンポーネント設計
  • stateとPropsの値によってDOMをレンダリング
  • JSXで書く

の3つを体感で押さえられればこの先の理解も早いと思います。
今回は全体の流れの把握ができて、かつ挫折しない程度(笑)まで内容を絞っていますので、次回はもう少し応用編となります。

続きの記事:React初心者のまとめ(基本編:2/3)