たった2ステップ!React.js で選択画像をプレビューする方法(サンプルDL可)

さてさて、前回記事LaravelではじめてのReact.JSでは有名JSフレームワークReact.JSLaravelで使えるようにし、簡単なコンポーネントを作ってみました。

個人的にはメインの開発ではVue.jsを使っているのですが、せっかくなのでReact.JSで何か少し実用的なものをつくってみたくなりました。

そこで、今回は過去に好評をいただいた記事、たった2ステップ!Vue.js で選択画像をプレビューする方法(サンプルDL可)と同じ機能をReact.JSでもつくってみます。

もちろん、最後ではソースコードをダウンロードすることができます。

ぜひ開発の参考にしてくださいね。

※ コンパイル環境: Laravel Mix(設定方法はLaravelではじめてのReact.JSをご覧ください)

やりたいこと

  • <input type=”file”>で画像を選択したら、すぐ下にプレビュー画像を表示する
  • 画像が変更になったらプレビューも更新する
  • リセットボタンをつける

では実際に開発していきましょう!

レイアウト部分をつくる

まずは、resource/js/components/フォルダにPreviewInput.jsというファイルを作成し中身を次のようにします。

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

export default class PreviewInput extends Component {

    render() {

        return (
            <div>
                <input type="file" accept="image/*" />
                <div>
                    <img src="/images/example.png" />
                </div>
            </div>
        )

    }

}

ReactDOM.render(
    <PreviewInput />,
    document.getElementById('preview-input')
);

<input type="file">の下にプレビュー画像が表示されるブロック(現在は直接サンプル画像を指定しています)を設置します。

なお、呼び出し側のHTMLはこうなります。

<html>
<body>
    <div id="preview-input"></div>
    <script src="/js/app.js"></script>
</body>
</html>

では一度コンパイルしてレイアウトを見てみましょう。

React.js部分をつくる

では続いてReact.js(JavaScript)部分です。

コンストラクタで画像を格納する変数をつくる

まずはPreviewInputが呼び出された直後にthis.stateimageDataという名前の変数を作ります。ここには後ほど<input type="file">で選択された画像データが入ってくることになります。

constructor(props) {

    super(props)

    this.state = {
        imageData: null
    }

}

画像が選択されたときのイベントをつくる

ではその画像が選択されたときのイベント部分をつくっていきましょう。

必要になるのはonChangeイベントです。
この中でonFileChange()へイベント変数を渡しています。

return (
    <div>
        <input type="file" accept="image/*" onChange={
            (e) => {
                this.onFileChange(e)
            }
        } />
        <div>
            <img src="/images/example.png" />
        </div>
    </div>
)

続いてイベント変数を受け取るonFileChange()の中身です。

onFileChange(e) {

    const files = e.target.files

    if(files.length > 0) {

        var file = files[0]
        var reader = new FileReader()
        reader.onload = (e) => {

            this.setState({ imageData: e.target.result })

        };
        reader.readAsDataURL(file)

    } else {

        this.setState({ imageData: null })

    }

}

やっていることは、まず画像が本当に選択されたかどうかをファイル数でチェックし、もし選択されていたらFileReader()でその画像を読み込みます。

そして、読み込まれた画像データは先ほどコンストラクタで設定したimageDataの中へ格納することになります。

また、キャンセルされた場合(ファイル数がゼロのとき)はimageDatanullで初期化します。

プレビュー画像を表示する部分をつくる

では最後にプレビュー画像を表示する部分です。
一番最初につくったrender()にコードを追加します。

render() {

    const imageData = this.state.imageData
    let preview = ''

    if(imageData != null) {

        preview = (
            <div>
                <img src={imageData}/>
            </div>
        )

    }

    return (
        <div>
            <input type="file" accept="image/*" onChange={
                (e) => {
                    this.onFileChange(e)
                }
            } />
            {preview}
        </div>
    )

}

この中ではまず、画像データがnullではない(つまり選択された画像がある)場合にpreviewという変数に画像表示タグを格納し、最終的に{preview}の場所へ挿入します。

こうすることで、画像が選択されていないときは何も表示せず、画像が選択されたらプレビューを表示することができるようになります。

では実際にコンパイルしてテストしてみましょう。

今回は少し小さなプロフィール画像で試してみましたが、うまく表示されました。

リセットボタンをつくる

では最後にプレビュー画像が表示されたらその下にリセットボタンを表示し、クリックされたら選択画像をクリアします。

まずは、クリックされたときに<input type="file">を参照できるようコンストラクタで専用のrefを作ります。

constructor(props) {

    // 省略
    this.fileInput = React.createRef()

}

そして、ファイル選択インプットにrefプロパティとして設定します。

<input type="file" accept="image/*" ref={this.fileInput} ...

これで、どこからでもこのファイル選択インプットへアクセスすることができるようになりました。

ではリセットボタンを作りましょう。

render() {

    // 省略

    let resetButton = ''

    if(imageData != null) {

        // 省略

        resetButton = (
            <div>
                <button type="button" onClick={this.resetInput.bind(this)}>リセットする</button>
            </div>
        )

    }

    return (
        <div>
            <!--省略-->
            {preview}
            {resetButton}
        </div>
    )

}

まず、リセットボタンを格納する変数resetButtonを作り、プレビュー画像と同じく画像データが存在している場合だけ表示するようにします。

そして、リセットボタンにはクリックイベントでresetInput()を実行するようにしますが、ここで重要なのが.bind(this)の部分です。これは、resetInput()内でthisを参照する必要があるためです。

では最後にresetInput()の中身です。

resetInput() {

    this.fileInput.current.value = ''
    this.setState({ imageData: null })

}

this.fileInput.currentがファイル選択インプットなので、valueを空白にしてクリア。そして、これだけではプレビュー画像は表示されたままなので、setState()で画像データを初期化します。

テストしてみる

では最後にテストしてみましょう。

まずはファイル選択インプットが表示されますのでクリックしてダイアログを開き画像を選択します。

するとインプットの下にプレビュー画像とリセットボタンがそれぞれ表示されます。

そして、リセットボタンを押すとクリアされます。

お疲れ様でした!

ソースコードをダウンロードする

今回実際に開発したソースコードを以下からダウンロードすることができます。(ちなみにIE 11でも確認済です)

※ ただし、Laravel Mixなどコンパイル設定はご自身で行ってください。

React.js で選択画像をプレビューする

おわりに

とうことで今回はReactで選択された画像のプレビューを表示するコードをつくってみました。

まだReactには慣れていないこともありますが、雑感としてはVueより書かないといけないコードが少し多いかなー、と感じました。

ただ、やはりコードがひとつひとつ分割されている構成なので可読性はいいですし、やはりバーチャルdomという考え方も革新的だと感じました。

まだまだReactの探求は続きそうです。

ぜひご期待下さい!

ではでは〜。