今回やること
前回は、一覧・新規登録・編集の3画面を
ListScreen、CreateScreen、
EditScreenというコンポーネントに分けました。
ただし、3つのコンポーネントはまだ
すべてApp.jsxの中に書かれています。
今回は、増えてきたコードを 役割ごとのファイルへ整理します。
今回はコードの置き場所を整理する回なので、 アプリの見た目や動作は変えません。
公開日:更新日:
前回は、一覧・新規登録・編集の3画面を
ListScreen、CreateScreen、
EditScreenというコンポーネントに分けました。
ただし、3つのコンポーネントはまだ
すべてApp.jsxの中に書かれています。
今回は、増えてきたコードを 役割ごとのファイルへ整理します。
今回はコードの置き場所を整理する回なので、 アプリの見た目や動作は変えません。
今回は、最終的に次のような構成にします。
src/
├─ App.jsx
├─ components/
│ ├─ ListScreen.jsx
│ ├─ CreateScreen.jsx
│ └─ EditScreen.jsx
├─ initialItems.js
└─ options.js
それぞれのファイルには、次の役割を持たせます。
App.jsx:画面切り替えを管理するcomponents:コンポーネントファイルを置くフォルダinitialItems.js:一覧に最初から表示するデータoptions.js:新規登録画面や編集画面で使う選択肢
JSXを書くコンポーネントのファイルは.jsx、
JavaScriptのファイルは.jsにしてください。
まずは、前回作った
ListScreenコンポーネントを別ファイルへ移してみましょう。
srcの中にcomponentsフォルダを作り、
その中にListScreen.jsxを作成します。
前回App.jsxの下側に書いた
ListScreenコンポーネントの定義を、
ListScreen.jsxファイルへ移します。
別ファイルから使えるようにするため、
今回はコンポーネントの先頭に
export defaultを付けます。
export default function ListScreen({ items, onCreate, onEdit }) {
return (
<main className="list-screen">
<header className="list-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" onClick={onCreate}>
新規登録
</button>
<button type="button" onClick={onEdit}>
編集
</button>
</div>
</header>
<h2 className="screen-title">一覧画面</h2>
<table>
<thead>
<tr>
<th>名前</th>
<th>カテゴリ</th>
<th>ステータス</th>
<th>価格</th>
<th>メモ</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.category}</td>
<td>{item.status}</td>
<td>{item.price}円</td>
<td>{item.memo}</td>
</tr>
))}
</tbody>
</table>
</main>
);
}
移したあとは、
App.jsxに書いてあった
ListScreenの定義は削除してください。
JavaScriptでは、あるファイルで定義した値や関数を
別のファイルから使えるようにするとき、
exportを使います。
今回は、ListScreenを
別のファイルから使えるようにするため、
次のように書いています。
export default function ListScreen(...) {
...
}
export defaultしたものは、
別のファイルからimportして使えます。
ListScreenコンポーネントを別ファイルへ移したので、
今度はApp.jsxから読み込みます。
App.jsxの先頭に、次の1行を追加します。
import ListScreen from "./components/ListScreen";
これで、別ファイルにあるListScreenコンポーネントを
App.jsxから使えるようになります。
App側の使い方は、前回と同じです。
return (
<ListScreen
items={items}
onCreate={() => setScreen("create")}
onEdit={() => setScreen("edit")}
/>
);
ファイルを分けても、
itemsや関数をpropsで渡す流れは変わりません。
ここで一度ファイルを保存して、ブラウザを確認してみましょう。 一覧画面がこれまでと同じように表示されればOKです。
次に、前回App.jsxに書いた
CreateScreenコンポーネントも別ファイルへ移します。
src/components/CreateScreen.jsxを作成し、
CreateScreenの定義を
App.jsxから移動してください。
こちらも別ファイルから使えるように、
export defaultを付けます。
export default function CreateScreen({ onBack }) {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={onBack}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title">新規登録画面</h2>
</main>
);
}
移したあとは、
App.jsxに書いてあった
CreateScreenの定義を削除します。
そして、App.jsxの先頭で読み込みます。
import CreateScreen from "./components/CreateScreen";
Appからの使い方は前回と同じです。
if (screen === "create") {
return <CreateScreen onBack={() => setScreen("list")} />;
}
編集画面も同じように、
前回App.jsxに書いた
EditScreenコンポーネントを別ファイルへ移します。
src/components/EditScreen.jsxを作成し、
EditScreenの定義を移動してください。
こちらも先頭にexport defaultを付けます。
export default function EditScreen({ onBack }) {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={onBack}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title edit-title">編集画面</h2>
</main>
);
}
移したあとは、
App.jsxに書いてあった
EditScreenの定義を削除します。
App.jsxの先頭で読み込みます。
import EditScreen from "./components/EditScreen";
これで、3つの画面コンポーネントを
App.jsxから別ファイルへ移せました。
ファイルを保存して、 「新規登録」「編集」「一覧へ」で 3画面をこれまでと同じように切り替えられることを確認してみましょう。
次は、これまでAppの中に置いていた初期データも別ファイルへ整理します。
srcの中にinitialItems.jsを作成し、App.jsxにある初期データを移しましょう。
変数名はinitialItemsとしてください。
export const initialItems = [
{
id: "sample-1",
name: "はじめてのWebデザイン本",
category: "本",
status: "気になる",
price: 2200,
memo: "図が多くて読みやすそう",
},
{
id: "sample-2",
name: "小さなサイドテーブル",
category: "家具",
status: "欲しい",
price: 3500,
memo: "部屋に置きやすそう",
},
];
移したあとは、
App.jsxに書いてあった初期データは削除します。
ここではexport defaultではなく、
export constを使っています。
App.jsxでは、
次のように{}を使って読み込みます。
import { initialItems } from "./initialItems";
読み込んだinitialItemsを、
一覧画面へ渡すデータとして使います。
return (
<ListScreen
items={initialItems}
onCreate={() => setScreen("create")}
onEdit={() => setScreen("edit")}
/>
);
初期データの置き場所は変わりましたが、
ListScreenにはこれまでと同じように
propsでデータを渡しています。
ファイルを保存してブラウザを確認し、 2件のデータがこれまでと同じように表示されればOKです。
次回は、新規登録画面にフォームを作ります。
その後の編集画面でも、 カテゴリやステータスには同じ選択肢を使います。
同じ選択肢をそれぞれの画面に直接書くのではなく、
共通して使えるように
options.jsへまとめておきましょう。
export const categories = [
"本",
"漫画",
"映画",
"アニメ",
"ゲーム",
"音楽",
"家具",
"その他",
];
export const statuses = [
"とりあえずメモ",
"気になる",
"欲しい",
"検討中",
"購入済",
];
categoriesにはカテゴリの選択肢、
statusesにはステータスの選択肢をまとめています。
今回登場した内容を、 ReactとJavaScriptに分けて整理します。
| 種類 | 今回登場したもの |
|---|---|
| React | 別ファイルに分けたコンポーネントを組み合わせて使う |
| JavaScript | export、import、JavaScriptモジュール |
コンポーネントを組み合わせて画面を作る部分はReactです。
一方、別ファイルの値や関数を読み書きする
exportとimportはJavaScriptの機能です。
componentsフォルダへ分けたexportで別ファイルから使えるようにしたimportで別ファイルのコンポーネントやデータを読み込んだinitialItems.jsへ分けたoptions.jsへまとめた
今回、作成・変更したファイルの完成コードです。
※ファイル名やファイルを置く場所を間違えないように注意してください
import { useState } from "react";
import ListScreen from "./components/ListScreen";
import CreateScreen from "./components/CreateScreen";
import EditScreen from "./components/EditScreen";
import { initialItems } from "./initialItems";
export default function App() {
const [screen, setScreen] = useState("list");
if (screen === "create") {
return <CreateScreen onBack={() => setScreen("list")} />;
}
if (screen === "edit") {
return <EditScreen onBack={() => setScreen("list")} />;
}
return (
<ListScreen
items={initialItems}
onCreate={() => setScreen("create")}
onEdit={() => setScreen("edit")}
/>
);
}
export default function ListScreen({ items, onCreate, onEdit }) {
return (
<main className="list-screen">
<header className="list-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" onClick={onCreate}>
新規登録
</button>
<button type="button" onClick={onEdit}>
編集
</button>
</div>
</header>
<h2 className="screen-title">一覧画面</h2>
<table>
<thead>
<tr>
<th>名前</th>
<th>カテゴリ</th>
<th>ステータス</th>
<th>価格</th>
<th>メモ</th>
</tr>
</thead>
<tbody>
{items.map((item) => (
<tr key={item.id}>
<td>{item.name}</td>
<td>{item.category}</td>
<td>{item.status}</td>
<td>{item.price}円</td>
<td>{item.memo}</td>
</tr>
))}
</tbody>
</table>
</main>
);
}
export default function CreateScreen({ onBack }) {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={onBack}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title">新規登録画面</h2>
</main>
);
}
export default function EditScreen({ onBack }) {
return (
<main className="form-screen">
<header className="screen-header">
<h1>気になるものリスト</h1>
<div className="header-actions">
<button type="button" className="secondary" onClick={onBack}>
一覧へ
</button>
</div>
</header>
<h2 className="screen-title edit-title">編集画面</h2>
</main>
);
}
export const initialItems = [
{
id: "sample-1",
name: "はじめてのWebデザイン本",
category: "本",
status: "気になる",
price: 2200,
memo: "図が多くて読みやすそう",
},
{
id: "sample-2",
name: "小さなサイドテーブル",
category: "家具",
status: "欲しい",
price: 3500,
memo: "部屋に置きやすそう",
},
];
export const categories = [
"本",
"漫画",
"映画",
"アニメ",
"ゲーム",
"音楽",
"家具",
"その他",
];
export const statuses = [
"とりあえずメモ",
"気になる",
"欲しい",
"検討中",
"購入済",
];