iimon TECH BLOG

iimonエンジニアが得られた経験や知識を共有して世の中をイイモンにしていくためのブログです

React初心者向け!不動産広告を作成してみる

■はじめに

こんにちは!

株式会社iimonでフロントエンドを担当しているなかむです!

私事ですが、最近5daysインターンが有り、大学生がiimonに来てくれました!

私ともう一人の同期がメンターとなり、大学生と一緒にReactで不動産広告を作成しました。

Reactは今まで触ったことがなかったので、その再現が一人でできるか復習も兼ねてこのテーマを選ばせていただきました!

対象読者はHTML, CSS, JavaScript経験者、Reactは未経験 or 経験が浅い方です。どうぞよろしくお願いしますm( _ _ )m

■Reactとは何か?

ReactはJavaScriptライブラリで、ユーザーインターフェース(UI)を構築するために使用されます。

主な特徴としては、

  • コンポーネントベース

ReactはUIをコンポーネントという小さな再利用可能な部品に分割します。これにより、コードの管理と再利用が容易になります。

  • 仮想DOM

Reactは宣言的なコードスタイルを採用しており、UIの状態をどのように表示するかを明確に記述できます。これにより、コードの予測可能性とデバッグのしやすさが向上します。

  • 宣言的UI

Reactは宣言的なコードスタイルを採用しており、UIの状態をどのように表示するかを明確に記述できます。これにより、コードの予測可能性とデバッグのしやすさが向上します。

などがあります。

Reactを使用することで、モダンでインタラクティブウェブアプリケーションを効率よく開発することができます。

react.dev のご紹介

■環境構築

リポジトリをCloneする

// SSH
❯ git clone git@github.com:yukanakamura1226/iimon-tech-blog-react.git

// HTTPS
❯ git clone https://github.com/yukanakamura1226/iimon-tech-blog-react.git

まずは、環境構築のためサンプルリポジトリをCloneしてきます。

package.jsonがあるディレクトリに移動します。

cd iimon-tech-blog-react/

npm installを実行して、パッケージをインストールします。

npm run devを実行して、viteを起動します。

このURLをクリックすると、このようなページが出てくると思います。

■解説

◆Reactの初期化

作成したディレクトリは画像のようになっていると思います。

Form.jsxが青色のフォーム部分、Advertisement.jsxが緑色の不動産広告部分に当たります。

index.htmlの読み込み

ブラウザがindex.htmlを読み込みます。このファイルには、Reactアプリケーションがマウントされるための<div id="root"></div>が含まれています。

JavaScriptの読み込み

<script type="module" src="/src/main.jsx"></script>タグにより、main.jsxが読み込まれます。ここでReactのエントリーポイントが定義されています。

Reactの初期化

main.jsx内で、createRoot(document.getElementById("root"))が呼び出され、Reactのレンダリングツリーがrootにマウントされます。

コンポーネントレンダリング

Appコンポーネント<StrictMode>内でレンダリングされます。Appコンポーネントは他の子コンポーネント(例: FormやAdvertisement)を含んでおり、それらも順次レンダリングされます。

レンダリングの再実行

コンポーネントが状態やプロパティの変更を検知すると、必要な部分のみが再レンダリングされます。Reactの仮想DOMがこのプロセスを効率的に行います。

この流れにより、Reactアプリケーションは効率的に初期化され、ユーザーの操作に応じてインタラクティブに更新されます。

index.htmlはアプリケーションの基盤を提供し、実際のロジックやインタラクションはJavaScriptファイル内で管理されます。

◆App.jsxの説明

import "./App.css";
import Advertisement from "./components/Advertisement/Advertisement";
import Form from "./components/Form/Form";
import { useForm } from "react-hook-form";

import Advertisementimport Form これらは、他のファイルからAdvertisementとFormというコンポーネントをインポートしています。

それぞれのコンポーネントは、別のファイルで定義されており、再利用可能なUI部品です。

import { useForm } from "react-hook-form";

react-hook-formライブラリからuseFormというフックをインポートしています。このフックは、フォームの状態管理を簡単にするためのものです。

function App() {
    const { register, watch } = useForm();
    const formData = watch();
    return (
        <div className="app-all">
            <Form register={register} formData={formData} className="form" />
            <Advertisement formData={formData} className="advertisement" />
        </div>
    );
}

export default App;

function App()

これはReactコンポーネントの定義です。Appという名前の関数コンポーネントを定義しています。

const { register, watch } = useForm();

register フォームの入力フィールドをReact Hook Formに登録するための関数です。

watch フォームの入力データをリアルタイムで監視し、その状態を取得するための関数です。

const formData = watch(); watch関数を使って、フォームデータを常に最新の状態で取得しています。

return (...) この部分は、コンポーネントが実際に画面に描画する内容を定義します。

<Form register={register} formData={formData} className="form" /> Formコンポーネントレンダリングし、registerとformDataをプロパティとして渡しています。

<Advertisement formData={formData} className="advertisement" /> Advertisementコンポーネントレンダリングし、formDataをプロパティとして渡しています。

Reactでコンポーネントにプロパティ(props)を渡すことには、いくつかの利点があります。

プロパティ(props)を渡すことで、親コンポーネントから子コンポーネントにデータを渡すことができます。これにより、子コンポーネントは親から提供されたデータを利用して、UIを動的に更新したり、表示内容を変更したりできます。

また、子コンポーネントが受け取ったデータを使って、親コンポーネントにフィードバックを返すことができます。たとえば、フォームの入力が変更されたときに、親コンポーネントにその変更を通知することができます。

export default App; 他のファイルからこのAppコンポーネントをインポートできるようにするためのエクスポート文です。

App.jsxではFormとAdvertisementという2つのコンポーネントを使って、フォームデータを管理し、それに基づいてUIを表示しています。

react-hook-formを使うことで、フォームの状態管理が非常に簡単になっています。

◆Form.jsxの説明

function Form({ register }) {
    return (
        <form>
            ...
        </form>
    );
}

export default Form;

function Form({ register }): これはFormという名前のReact関数コンポーネントを定義しています。このコンポーネントは、親コンポーネントからregisterというプロパティを受け取ります。

register:react-hook-formライブラリの関数で、フォームの各入力フィールドを登録するために使用します。この関数を使うことで、フォームのデータを簡単に管理できます。

<label htmlFor="address">住所</label>
<input id="address" {...register("address")} />

例えば、register("name")を使って、このフィールドをフォームに登録しています。

この画像を見るとForm.jsxで物件名を一文字入力すると、リアルタイムでレンダリングがされて、formDataがAdvertisementコンポーネントに送信されていることがわかります。

◆Advertisement.jsxの説明

インポートした、TextとPicturesという2つの子コンポーネントを使ってformDataを受け渡しています。

/* eslint-disable react/prop-types */
import Text from "./Text.jsx";
import Pictures from "./Pictures.jsx";
import "./Advertisement.css";

function Advertisement({ formData }) {
    return (
        <div className="advertisement">
            <Text formData={formData} className="text-content" />
            <Pictures formData={formData} className="pictures-content" />
        </div>
    );
}

export default Advertisement;

◆Text.jsxの説明

コンポーネント(Advertisement)から受け取ったformDataの値を表示します。

プロパティ名はForm.jsxで入力された...register()と対応しています。

/* eslint-disable react/prop-types */
import './Text.css'; 

function Text({ formData }) {
    return (
        <div className="text-container">
            <header>
                <h2 className="text-title">{formData.name}</h2>
                <p>{formData.address}</p>
            </header>
            <div className="text-table">
                <div className="text-row">
                    <div className="text-cell">価格</div>
                    <div className="text-cell">{formData.price}</div>
                </div>
                <div className="text-row">
                    <div className="text-cell">交通</div>
                    <div className="text-cell">駅徒歩 {formData.walkTime}</div>
                </div>
                <div className="text-row">
                    <div className="text-cell">間取り</div>
                    <div className="text-cell">{formData.floorPlan}</div>
                </div>
                <div className="text-row">
                    <div className="text-cell">物件の種類</div>
                    <div className="text-cell">{formData.propertyType}</div>
                </div>
                <div className="text-row">
                    <div className="text-cell">面積</div>
                    <div className="text-cell">{formData.area}</div>
                </div>
            </div>
        </div>
    );
}

export default Text;

◆Pictures.jsxの説明

このコンポーネントでは、物件の外装、内装、間取りの画像を表示します。

function Pictures({ formData }) {
    const createImageURL = (file) => {
        if (file && file[0] instanceof File) {
            return URL.createObjectURL(file[0]);
        }
        return null;
    };

    return (
        <div className="pictures-container">
            <div className="image-row">
                {createImageURL(formData.imageExterior) && (
                    <div className="image-item">
                        <p>外装</p>
                        <img
                            src={createImageURL(formData.imageExterior)}
                            alt="外装画像"
                        />
                    </div>
                )}
                {createImageURL(formData.imageInterior) && (
                    <div className="image-item">
                        <p>内装</p>
                        <img
                            src={createImageURL(formData.imageInterior)}
                            alt="内装画像"
                        />
                    </div>
                )}
                {createImageURL(formData.imageMap) && (
                    <div className="image-item">
                        <p>間取り</p>
                        <img
                            src={createImageURL(formData.imageMap)}
                            alt="間取り画像"
                        />
                    </div>
                )}
            </div>
        </div>
    );
}

createImageURL関数: 画像ファイルからURLを生成するための関数です。

file && file[0] instanceof File: fileが存在し、かつそれがFileオブジェクトであることを確認しています。

試しに外装画像を入れてみると、formData.imageExterior[0]に登録されていることが確認できます。

URL.createObjectURL(file[0]): 画像ファイルからブラウザで表示可能なURLを生成します。

ローカルのファイルオブジェクトはそのままではURLとして使用できないため、この関数を使ってブラウザ内で一時的にファイルを参照できるURLを生成する必要があります。

ちなみにすべての情報を入力すると、こんな感じのレイアウトになります。

■まとめ

ファイルのコードを1行ずつ読み解くことで、少しReactの基本的な仕組みがわかるようになりました!

他にも面白そうなライブラリや機能について学習していければなと思います。

ここまで読んでくださってありがとうございました。

また、弊社ではエンジニアを募集しております。

ぜひカジュアル面談でお話ししましょう!

ご興味ありましたら、ご応募ください!

Wantedly / Green