
九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、前回&前々回からVite
を使ったReact
の記事をお届けしましたが、いかがだったでしょうか。
個人的には長らくVue
を使っていたので、まだまだ効率化できていないコードがあるかもですが、ぜひ同じような学習者さんたちに楽しんでいただけたら嬉しいです。
そして、今回ですがReact
を使ったらやってみたいと考えていたある機能にすることにしました。
それは・・・・・・
画像のプレビュー機能
です。
つまり、以下のようなものですね。
- 画像を選択する
- その画像をページに表示(画像があっているか確認できる)
- 選択した画像は上書き&削除(キャンセル)することもできる
ということで、今回はReact
でプレビュー画像入力をつくってみたいと思います。
ぜひ何かの参考になりましたら嬉しいです。
「滋賀県の日野町、
いいところでした」
開発環境: Laravel 9.x、Vite、InertiaJS、React
前提として
Laravel
でVite
を使って開発ができることが前提です。
もしまだの方は以下の記事を参考にしてVite
が動くところまで準備しておいてください。
Vite + Inertia + React でログイン機能をインストールする
※ なお、ログイン機能は不要です。
ルートをつくる
では、まずはLaravel
に画像プレビュー用のページをルートにつくりましょう。
routes/web.php
// 省略
use Inertia\Inertia;
// 省略
Route::get('preview_image_input', fn() => Inertia::render('PreviewImageInput/Index'));
この中のrender()
にセットしているのが、これからresources/Pages
内につくることになる「ビュー」になります。
ビューをつくる
では、ルートでセットしたビューをつくっていきます。
resources/js/Pages/PreviewImageInput/Index.jsx
import React from "react";
import PreviewImageInput from "@/Components/PreviewImageInput";
export default function Index() {
const handleImageChange = e => { // 画像に変化があったら実行される
console.log(e);
};
return (
<div className="p-4">
<h1 className="font-bold mb-3">📝 React を使ったプレビューつき画像入力のサンプル</h1>
<PreviewImageInput
onImageChange={e => handleImageChange(e)}
/>
</div>
);
}
とてもシンプルな内容ですが、PreviewImageInput
は次でつくるコンポーネントになります。
コンポーネントをつくる
では、今回のメインになります。
ビュー内でセットしたコンポーネントをつくっていきましょう。
resources/js/Components/PreviewImageInput.jsx
import React, { useState } from 'react';
export default function PreviewImageInput(props) {
// Data
const label = props.label || '画像を選択してください';
const labelClassName = props.labelClassName || 'bg-gray-200 text-gray-700 text-sm font-bold py-2 px-4 rounded';
const imageClassName = props.imageClassName || 'w-48';
const accept = props.accept || 'image/jpeg,image/png';
const [imageData, setImageData] = useState(null);
// Methods
const handleFileChange = e => { // 画像が選択された
const files = e.target.files;
if (files.length > 0) {
const file = files[0];
const reader = new FileReader();
reader.onload = e => {
const imageData = e.target.result;
setImageData(imageData);
handleImageChange({
status: 'added',
file: file,
imageData: imageData
});
}
reader.readAsDataURL(file);
}
};
const handleImageDelete = () => { // 画像の削除
if(confirm('画像を削除します。よろしいですか?')) {
setImageData(null);
handleImageChange({
status: 'removed',
file: null,
imageData: null
});
}
};
const handleImageChange = e => { // 画像に変化があったとき
if(typeof props.onImageChange === 'function') { // 呼び出し側でイベントがセットされていれば実行
props.onImageChange(e);
}
};
return (
<>
<label className={labelClassName}>
{label}
<input
type="file"
accept={accept}
style={{ display: 'none' }}
onChange={e => handleFileChange(e)}
/>
</label>
{imageData &&
<div className="mt-5">
<img src={imageData} className={imageClassName} />
<button
type="button"
className="bg-red-500 text-gray-100 text-sm font-bold py-1 px-3 rounded mt-3"
onClick={e => handleImageDelete(e)}>削除</button>
</div>
}
</>
);
}
基本的にはよく用いられる、以下の流れをReact
で実装しているだけです。
- 画像を選択
- JavaScript 画像を読み込む
- 画像データを <img> タグへセット
なお、重要なのが以下の部分です。
props.onImageChange(e);
これは、props
を通して親の要素のイベントを実行しているんですね。(ここはホントにシンプルでいいですね)
つまり、Index.jsx
で言うと、以下の太字の部分が実行されるということになります。
resources/js/Pages/PreviewImageInput/Index.jsx
<PreviewImageInput
onImageChange={e => handleImageChange(e)}
/>
そのため、onImageChange
がセットされていない場合にはエラーになってしまうので、以下のIF文でチェックをしています。
if(typeof props.onImageChange === 'function') {
// イベントがセットされていれば実行
}
では、今回は短いですがこれで完了です。
お疲れ様でした。
企業様へのご提案
今回のVite
を利用した開発では、これまで必要だった「ビルド(構築)待ち」がほぼなくなるため、高速な開発が実現できます。
例えば、毎回ビルドに10秒必要だったとすると6回ビルドするだけで60秒=1分無駄にしていることになります。もし100回ビルドするとなると1000秒=16分40秒です。
16分あったら、小さなコンポーネントなら十分つくれます。
そして、これが月単位となると… or 開発人数が多くなると…
そうです。
その分不必要な時間が必要になるわけです。
ということで、ぜひVite
を使った構築をご希望でしたらお問い合わせからご相談ください。
お待ちしております。
デモページを作りました
テストしてみる
では、実際にテストしてみましょう。
まずは以下のコマンドでVite
を起動して、ブラウザで「https://******/preview_image_input」へアクセスします。
npm run dev
すると、以下のような表示になるので、ボタンをクリックして「この前行った、滋賀県のビール醸造所の写真 」を選択してみます。
すると・・・・・・
はい
うまくビールの写真が表示されました。
では、コンソールの方はどうなっているでしょうか。
はい
status
がadded
になっていて、画像データやファイル本体も取得できています。
それでは、このままの状態で別の写真(この前見に行った珍ポスト)を選択してみましょう。
どうなったでしょうか・・・・・・
はい画像が切り替わりました。
こちらも成功です。
では、最後に「削除」ボタンをクリックして画像が消えるかもチェックしてみましょう。
クリックしてみます。
すると・・・・・・???
画像が削除されました。
では、コンソールはどうでしょうか。
今回はstatus
がremoved
になっていて、画像&ファイルもnull
になっています。
すべて成功です
おわりに
ということで、今回はReact
でプレビュー画像つきの入力ができるように実装してみました。
個人的な今回のハイライトは、props.XXXXXX()
で親要素のイベントが実行できることを知った部分です。
Vue
ではemit
を使っていましたが、少しクセが強い部分だったので今回のようなコードはよりシンプルで好感触でした。
ぜひ皆さんもいろいろとやってみてくださいね。
ではでは〜
「滋賀県のぽけフタ、
全件ゲットしました」