diff --git a/docs/.vitepress/sidebarConfigs/chapters/chapter1/chapter1.ts b/docs/.vitepress/sidebarConfigs/chapters/chapter1/chapter1.ts index 5cba28cf..31a74c55 100644 --- a/docs/.vitepress/sidebarConfigs/chapters/chapter1/chapter1.ts +++ b/docs/.vitepress/sidebarConfigs/chapters/chapter1/chapter1.ts @@ -18,7 +18,15 @@ export const chapter1SidebarItems: DefaultTheme.SidebarItem[] = [ ...section1SidebarItems, ...section2SidebarItems, ...section3SidebarItems, - ...section4SidebarItems + ...section4SidebarItems, + { + text: 'React入門', + link: '/chapter1/dicts/react/0_react-intro' + }, + { + text: 'アプリを作ってみよう(React)', + link: '/chapter1/dicts/react/1_create-app' + } ] } ] diff --git a/docs/chapter1/dicts/react/0_react-intro.md b/docs/chapter1/dicts/react/0_react-intro.md new file mode 100644 index 00000000..3907ffc4 --- /dev/null +++ b/docs/chapter1/dicts/react/0_react-intro.md @@ -0,0 +1,223 @@ +# React 入門 + +React 編は本編を一通り終えた後に行うことを想定しているため、一部省略しています。本編が終わっていない方は先に本編を進めてください。 + +## React プロジェクトの作成 + +`~/develop`ディレクトリの中で以下のコマンドを実行してください。 +```bash +$ npm create vite@latest todolist -- --template react-ts -y + +Scaffolding project in /home/mehm8128/develop/todolist... + +Done. Now run: + + cd todolist + npm install + npm run dev + +``` + +プロジェクトが作成できたら、`cd {プロジェクト名}`でプロジェクトのディレクトリに移動し、VSCode で開いてください。 + +開いたプロジェクトの中に入っている`package.json`というファイルには npm に関する様々な設定が書かれています。 +この中には依存するパッケージ一覧も含まれており、以下のコマンドでそれらをインストールできます。 + +`$ npm i` +もしくは +`$ npm install` + +```bash +$ npm i + +added 218 packages, and audited 219 packages in 12s + +41 packages are looking for funding + run `npm fund` for details + +found 0 vulnerabilities +``` + +テンプレートは初期状態でビルド&配信できるようになっているので、以下のコマンドを実行してブラウザで確認してみましょう。 + +`$ npm run dev` + +```bash +$ npm run dev + +> todolist@0.0.0 dev +> vite + +Port 5173 is in use, trying another one... + + VITE v5.2.10 ready in 159 ms + + ➜ Local: http://localhost:5174/ + ➜ Network: use --host to expose + ➜ press h + enter to show help +``` + +この状態で、ブラウザから localhost:5173 にアクセスすると、以下のような画面が表示されるはずです。 + +![](images/0/vite-start.png) + +止めるときは`Ctrl + C`で止めてください。 + +また、このタイミングで Git の初期化もしておきましょう。`.git`ディレクトリがプロジェクト内に存在していないので、このままだと Git 管理ができません。 +```bash +$ git init +``` + +## React 入門 + +### React とは + +以下のリンクから公式ドキュメントに飛ぶことができます。 +[React](https://react.dev/) + +日本ではよく使われているフレームワークなのですが traP ではあんまり使われておらず、使われているのは traPCollection くらいです。 + +### `.tsx`ファイルについて + +Vue では`.vue`という拡張子でコンポーネントファイルを作成していましたが、React では`.tsx`という拡張子でコンポーネントファイルを作ることができます。 + +基本的には TypeScript を書いて、`return()`内に HTML(JSX)を書きます。 + + +#### Sample.tsx + +<<< @/chapter1/dicts/react/src/0/Sample.tsx + + +## React を書く準備 + +まず、以下の拡張機能をインストールしてください。 + +#### React Developer Tools + +Chrome Devtool に React 向けのデバッグ機能を追加してくれます。 +[React Developer Tools - Chrome ウェブストア](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi?hl=ja) + + +## React を書く + +### React を書く + +先にコードを書いてから解説を書いています。 +意味がわからなくてもとりあえずコピー&ペースト or 写経しましょう。 + +#### ファイルの作成 + +`components`ディレクトリを作成し、その中に`ClickCounter.tsx`というファイルを作成します。 + +![](images/0/clickcounter.png) + +#### ソースコードの変更 + +#### src/main.tsx + +CSS ファイルの読み込みを削除します。 + +<<< @/chapter1/dicts/react/src/0/main.tsx{tsx:line-numbers} + +##### src/pages/App.tsx + +`ClickCounter.tsx`を読み込み、カウンターを配置します。 + +<<< @/chapter1/dicts/react/src/0/App.tsx{tsx:line-numbers} + +##### src/components/ClickCounter.tsx + +<<< @/chapter1/dicts/react/src/0/ClickCounter.tsx{tsx:line-numbers} + +以下のように動けば OK です。 + +![](images/0/preview.gif) + +### ソースコード解説 + +#### src/pages/App.tsx + +##### 1 行目 + +```ts +import ClickCounter from "./components/ClickCounter.tsx" +``` + +`ClickCounter` コンポーネントを読み込む部分です。 + +##### 5-7行目 +テンプレート部分です。 +React のコンポーネントは 1 つのタグの中に収まっている必要があります。 +そのため、多くの場合 div タグか React.Fragment で囲まれています。 + +参考: [Fragment – React](https://react.dev/reference/react/Fragment) + +##### 6 行目 + +```tsx + +``` + +読み込んだコンポーネントを利用しています。 + +#### src/components/ClickCounter.tsx + +##### 4 行目 + +コンポーネント内で利用する変数を`useState`を用いてこのように書きます。引数には変数の初期値を与えます。 +ここでは`count`という名前の変数を`number`型で定義しています(実はこの程度なら TypeScript の型推論というものが効いて、初期値の`0`から自動で`count`変数は`number`型だと推論してくれます)。 +`useState`は返り値の配列の 0 番目に変数、1 番目に変数を更新するための関数が入っています。今回でいうと`count`が変数、`setCount`が変数を更新するための関数です。 +React はこの`setCount`が実行されたときに、引数に与えられた値で`count`の値を更新し、表示も更新(再描画)してくれます。 + +```ts + const [count, setCount] = useState(0) +``` + +参考 +[useState – React](https://react.dev/reference/react/useState) +[ジェネリクス (generics) | TypeScript入門『サバイバルTypeScript』](https://typescriptbook.jp/reference/generics) + +:::info +ここで`counter.js`の`countUp`を見てみましょう。 + +```js +const countUp = () => { + count++ + const countElement = document.querySelector('#count') + countElement.innerText = '回数: ' + count +} +``` + +`count`変数の値を変更した後に、DOM を直接操作して回数の値を更新しています。 +React では先ほど述べたように、`useState`を使うことで「値を更新」と「表示を変更」の 2 つをセットでやってくれるようになります。 + +::: + +##### 7 行目 + +JSXでは`{}`内に JavaScript が書けるので、count 変数の中身を表示しています。 + +```tsx +
回数: {count}
+``` + +参考: [JavaScript in JSX with Curly Braces – React](https://react.dev/learn/javascript-in-jsx-with-curly-braces) + + +##### 8・9 行目 + +ボタンが押されたイベントに対する処理を書いています。 +`useState`で定義した`setCount`関数に引数を渡すことで、`count`変数の中身を更新できます。 +また、`onClick`では今回のように直接 JavaScript を記述するだけでなく、`return()`外で定義した関数の呼び出しもできます。 + + +```tsx + + +``` + +参考 +[イベントへの入門 - ウェブ開発を学ぶ | MDN](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Building_blocks/Events) +[Responding to Events – React](https://react.dev/learn/responding-to-events) + diff --git a/docs/chapter1/dicts/react/1_create-app.md b/docs/chapter1/dicts/react/1_create-app.md new file mode 100644 index 00000000..29092212 --- /dev/null +++ b/docs/chapter1/dicts/react/1_create-app.md @@ -0,0 +1,154 @@ +# アプリを作ってみよう + +## 商品リストを作ってみる + +みなさんにはこの節の最後に Todo リストを作ってもらうのですが、商品リストをテーマに、Todo リストに必要な機能をピックアップしていきます。 + +こんな感じのを作っていきます。 +![](images/1/preview.gif) + +### 必要な要素を考える + +上の gif のようなアプリを実現するためには何が必要か考えてみましょう。 + +- 商品リストのコンポーネントを作る +- 商品のリストデータを保存する +- 商品のリストデータを表示する +- 商品を追加できる +- 商品の値段が 500 円以上だったら赤くする +- 商品の値段が 1000 円以上だったら「高額商品」と表示する + +こんな感じでしょうか。 +それでは上から順番に実装していきましょう。 + +### 商品リストのコンポーネントを作る + +`components`ディレクトリに`ItemList.tsx`というファイルを作成します。 +![](images/1/directory.png) + +#### src/components/ItemList.tsx + +中身はコンポーネントに最低限必要な部分だけ書きます。 + +<<< @/chapter1/dicts/react/src/1/ItemListInit.tsx{tsx:line-numbers} + +#### App.tsx + +<<< @/chapter1/dicts/react/src/1/App.tsx{tsx:line-numbers} + +表示されました。 +こうすることで、後は`ItemList.tsx`の中身を書き変えればよくなります。 + +![](images/1/itemlist-setup.png) + +### 商品のリストデータを保存する + +商品リストのデータを保存するのに適当な変数の型は何でしょうか? +商品「リスト」なので配列がよさそうです。 +というわけで、配列を使ってデータを保持することにします。 +今は商品の追加ができないので、とりあえずダミーデータを入れておきます。 + +参考 +[Array | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array) +[JavaScript オブジェクトの基本 - ウェブ開発を学ぶ | MDN](https://developer.mozilla.org/ja/docs/Learn/JavaScript/Objects/Basics) + +<<< @/chapter1/dicts/react/src/1/ItemListItems.tsx{tsx:line-numbers} + +4~7 行目は TypeScript の記法で、`Item`という型を`interface`を用いて定義しています。 +そして `useState` のジェネリクスに`Item[]`を渡すことで、`items`変数を`Item`型の配列として扱えるようにしています。 + +参考: [インターフェース (interface) | TypeScript 入門『サバイバル TypeScript』](https://typescriptbook.jp/reference/object-oriented/interface) + +### 商品のリストデータを表示する + +先ほど定義したリストの情報を表示していきます。 +React ではリストデータを for 文のようにループさせて表示させるには、`map`を使います。 +`map` を使うときには`key`を設定しなければいけません(理由(やや難): [Rendering Lists – React](https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key))。 + +参考: [Rendering Lists – React](https://react.dev/learn/rendering-lists) + +これを使ってデータを表示してみます。 + +<<< @/chapter1/dicts/react/src/1/ItemListList.tsx{tsx:line-numbers} + +表示できました。 + +![](images/1/itemlist.png) + +### 商品を追加する + +フォームを使って、入力された文字を変数に格納します。 +参考: [input – React](https://react.dev/reference/react-dom/components/input#controlling-an-input-with-a-state-variable) + +これを使って商品を追加できるようにしてみます。 + +<<< @/chapter1/dicts/react/src/1/ItemListAdd.tsx{tsx:line-numbers} + +参考: [アロー関数式 | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions/Arrow_functions) + +できました! + +![](images/1/add-item.gif) + +#### 練習問題 1:商品リストに機能を追加 + +このままだとボタンを連打して商品の追加ができてしまいます。 + +- ボタンを押したら入力欄を空にする機能 +- 入力欄が空だったらボタンを押しても追加されないようにする機能 + +を追加してみましょう。 + +### 商品の値段が 500 円以上だったら赤くする + +条件が満たされたときだけ CSS を当てるようにしてみます。 +`items`配列を展開するところで。三項演算子を用いて条件を満たしたときにだけ`className`がつくようにします。 + +参考: [CSS の基本 | MDN](https://developer.mozilla.org/ja/docs/Learn/Getting_started_with_the_web/CSS_basics) +参考: [条件 (三項) 演算子 - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Conditional_operator) +参考: [css-modules/css-modules: Documentation about css-modules](https://github.com/css-modules/css-modules) + +<<< @/chapter1/dicts/react/src/1/ItemListRed.tsx{2,26} + +そして、`src/components/ItemList.module.css`を作成して以下の内容を記入します。 + +<<< @/chapter1/dicts/react/src/1/ItemList.module.css{tsx:line-numbers} + +![](images/1/red.png) + +### 商品の値段が 10000 円以上だったら「高額商品」と表示する + +ある特定の条件を満たした場合のみ、対象コンポーネントを表示するという機能を`&&`を使って実現します。もちろん、三項演算子を使っても実現できます。 + +参考: [Conditional Rendering – React](https://react.dev/learn/conditional-rendering) + +これを使って商品の値段が 10000 円以上だったら「高額商品」と表示するという機能を実現してみましょう。 + +<<< @/chapter1/dicts/react/src/1/ItemListExpensive.tsx{29} + +![](images/1/expensive.png) + +これで商品リストが完成しました! + +## Todo リストを作る + +ここまで紹介してきた機能を使うことで Todo リストが作れるはずです。 +頑張りましょう! + +#### 練習問題 2:Todo リストを作る + +Todo リストを作りましょう。 + +必要な機能は以下の通りです。 + +- タスクは未完または完了済みの状態を持つ。 +- タスクはタスク名を持つ。 +- 未完タスクのリストと完了済みタスクのリストが表示される。 +- タスクを完了させることができる。 +- タスクの追加ができる。 + +以上の機能が実現されていれば後は自由です。 +スタイルが気になる人は CSS なども書きましょう。 + +## 制作物を公開する +Vue の Todo リストを公開したときと同様の方法で公開できるはずです。やってみましょう。 diff --git a/docs/chapter1/dicts/react/images/0/clickcounter.png b/docs/chapter1/dicts/react/images/0/clickcounter.png new file mode 100644 index 00000000..9b76e0ea Binary files /dev/null and b/docs/chapter1/dicts/react/images/0/clickcounter.png differ diff --git a/docs/chapter1/dicts/react/images/0/preview.gif b/docs/chapter1/dicts/react/images/0/preview.gif new file mode 100644 index 00000000..79241de2 Binary files /dev/null and b/docs/chapter1/dicts/react/images/0/preview.gif differ diff --git a/docs/chapter1/dicts/react/images/0/vite-start.png b/docs/chapter1/dicts/react/images/0/vite-start.png new file mode 100644 index 00000000..0b014039 Binary files /dev/null and b/docs/chapter1/dicts/react/images/0/vite-start.png differ diff --git a/docs/chapter1/dicts/react/images/1/add-item.gif b/docs/chapter1/dicts/react/images/1/add-item.gif new file mode 100644 index 00000000..a033f986 Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/add-item.gif differ diff --git a/docs/chapter1/dicts/react/images/1/directory.png b/docs/chapter1/dicts/react/images/1/directory.png new file mode 100644 index 00000000..2f3cc587 Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/directory.png differ diff --git a/docs/chapter1/dicts/react/images/1/expensive.png b/docs/chapter1/dicts/react/images/1/expensive.png new file mode 100644 index 00000000..760f7df3 Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/expensive.png differ diff --git a/docs/chapter1/dicts/react/images/1/itemlist-setup.png b/docs/chapter1/dicts/react/images/1/itemlist-setup.png new file mode 100644 index 00000000..13f999f9 Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/itemlist-setup.png differ diff --git a/docs/chapter1/dicts/react/images/1/itemlist.png b/docs/chapter1/dicts/react/images/1/itemlist.png new file mode 100644 index 00000000..7bdc7c97 Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/itemlist.png differ diff --git a/docs/chapter1/dicts/react/images/1/preview.gif b/docs/chapter1/dicts/react/images/1/preview.gif new file mode 100644 index 00000000..7aa17d0b Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/preview.gif differ diff --git a/docs/chapter1/dicts/react/images/1/red.png b/docs/chapter1/dicts/react/images/1/red.png new file mode 100644 index 00000000..d44369ef Binary files /dev/null and b/docs/chapter1/dicts/react/images/1/red.png differ diff --git a/docs/chapter1/dicts/react/src/0/App.tsx b/docs/chapter1/dicts/react/src/0/App.tsx new file mode 100644 index 00000000..a128084c --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/App.tsx @@ -0,0 +1,11 @@ +import ClickCounter from './components/ClickCounter' + +function App() { + return ( +
+ +
+ ) +} + +export default App diff --git a/docs/chapter1/dicts/react/src/0/ClickCounter.tsx b/docs/chapter1/dicts/react/src/0/ClickCounter.tsx new file mode 100644 index 00000000..2c636238 --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/ClickCounter.tsx @@ -0,0 +1,12 @@ +import { useState } from 'react' + +export default function ClickCounter() { + const [count, setCount] = useState(0) + return ( +
+
回数: {count}
+ + +
+ ) +} diff --git a/docs/chapter1/dicts/react/src/0/Sample.tsx b/docs/chapter1/dicts/react/src/0/Sample.tsx new file mode 100644 index 00000000..a8f74afb --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/Sample.tsx @@ -0,0 +1,4 @@ +function Home() { + // ロジック + return
見た目
+} diff --git a/docs/chapter1/dicts/react/src/0/counter.js b/docs/chapter1/dicts/react/src/0/counter.js new file mode 100644 index 00000000..f89cd904 --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/counter.js @@ -0,0 +1,12 @@ +let count = 0 +const countUp = () => { + count++ + const countElement = document.querySelector('#count') + countElement.innerText = '回数: ' + count +} + +const reset = () => { + count = 0 + const countElement = document.querySelector('#count') + countElement.innerText = '回数: ' + count +} diff --git a/docs/chapter1/dicts/react/src/0/index.html b/docs/chapter1/dicts/react/src/0/index.html new file mode 100644 index 00000000..6ba5daac --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/index.html @@ -0,0 +1,6 @@ + +

カウンター

+
回数: 0
+ + + diff --git a/docs/chapter1/dicts/react/src/0/main.tsx b/docs/chapter1/dicts/react/src/0/main.tsx new file mode 100644 index 00000000..ed319314 --- /dev/null +++ b/docs/chapter1/dicts/react/src/0/main.tsx @@ -0,0 +1,9 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import App from './App.tsx' + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + +) diff --git a/docs/chapter1/dicts/react/src/1/App.tsx b/docs/chapter1/dicts/react/src/1/App.tsx new file mode 100644 index 00000000..901d2d3a --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/App.tsx @@ -0,0 +1,13 @@ +import ClickCounter from './components/ClickCounter' +import ItemList from './components/ItemList' + +function App() { + return ( +
+ + +
+ ) +} + +export default App diff --git a/docs/chapter1/dicts/react/src/1/ItemList.module.css b/docs/chapter1/dicts/react/src/1/ItemList.module.css new file mode 100644 index 00000000..a7222d41 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemList.module.css @@ -0,0 +1,3 @@ +.over500 { + color: red; +} diff --git a/docs/chapter1/dicts/react/src/1/ItemListAdd.tsx b/docs/chapter1/dicts/react/src/1/ItemListAdd.tsx new file mode 100644 index 00000000..b922bdc2 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListAdd.tsx @@ -0,0 +1,48 @@ +import { useState } from 'react' + +interface Item { + name: string + price: number +} + +export default function ItemList() { + const [items, setItems] = useState([ + { name: 'たまご', price: 100 }, + { name: 'りんご', price: 160 } + ]) + const [newItemName, setNewItemName] = useState('') + const [newItemPrice, setNewItemPrice] = useState(0) + + const addItem = () => { + setItems([...items, { name: newItemName, price: newItemPrice }]) + } + + return ( +
+
ItemList
+
+ {items.map((item) => ( +
+
名前:{item.name}
+
{item.price}円
+
+ ))} +
+
+ + + +
+
+ ) +} diff --git a/docs/chapter1/dicts/react/src/1/ItemListExpensive.tsx b/docs/chapter1/dicts/react/src/1/ItemListExpensive.tsx new file mode 100644 index 00000000..e63bb2f7 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListExpensive.tsx @@ -0,0 +1,50 @@ +import { useState } from 'react' +import styles from './ItemList.module.css' + +interface Item { + name: string + price: number +} + +export default function ItemList() { + const [items, setItems] = useState([ + { name: 'たまご', price: 100 }, + { name: 'りんご', price: 160 } + ]) + const [newItemName, setNewItemName] = useState('') + const [newItemPrice, setNewItemPrice] = useState(0) + + const addItem = () => { + setItems([...items, { name: newItemName, price: newItemPrice }]) + } + + return ( +
+
ItemList
+
+ {items.map((item) => ( +
= 500 ? styles.over500 : undefined}> +
名前:{item.name}
+
{item.price}円
+ {item.price >= 10000 &&
高額商品
} +
+ ))} +
+
+ + + +
+
+ ) +} diff --git a/docs/chapter1/dicts/react/src/1/ItemListInit.tsx b/docs/chapter1/dicts/react/src/1/ItemListInit.tsx new file mode 100644 index 00000000..54addf98 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListInit.tsx @@ -0,0 +1,3 @@ +export default function ItemList() { + return
ItemList
+} diff --git a/docs/chapter1/dicts/react/src/1/ItemListItems.tsx b/docs/chapter1/dicts/react/src/1/ItemListItems.tsx new file mode 100644 index 00000000..8985ba06 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListItems.tsx @@ -0,0 +1,15 @@ +import { useState } from 'react' + +interface Item { + name: string + price: number +} + +export default function ItemList() { + const [items, setItems] = useState([ + { name: 'たまご', price: 100 }, + { name: 'りんご', price: 160 } + ]) + + return
ItemList
+} diff --git a/docs/chapter1/dicts/react/src/1/ItemListList.tsx b/docs/chapter1/dicts/react/src/1/ItemListList.tsx new file mode 100644 index 00000000..40cc5026 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListList.tsx @@ -0,0 +1,27 @@ +import { useState } from 'react' + +interface Item { + name: string + price: number +} + +export default function ItemList() { + const [items, setItems] = useState([ + { name: 'たまご', price: 100 }, + { name: 'りんご', price: 160 } + ]) + + return ( +
+
ItemList
+
+ {items.map((item) => ( +
+
名前:{item.name}
+
{item.price}円
+
+ ))} +
+
+ ) +} diff --git a/docs/chapter1/dicts/react/src/1/ItemListRed.tsx b/docs/chapter1/dicts/react/src/1/ItemListRed.tsx new file mode 100644 index 00000000..d045ad73 --- /dev/null +++ b/docs/chapter1/dicts/react/src/1/ItemListRed.tsx @@ -0,0 +1,49 @@ +import { useState } from 'react' +import styles from './ItemList.module.css' + +interface Item { + name: string + price: number +} + +export default function ItemList() { + const [items, setItems] = useState([ + { name: 'たまご', price: 100 }, + { name: 'りんご', price: 160 } + ]) + const [newItemName, setNewItemName] = useState('') + const [newItemPrice, setNewItemPrice] = useState(0) + + const addItem = () => { + setItems([...items, { name: newItemName, price: newItemPrice }]) + } + + return ( +
+
ItemList
+
+ {items.map((item) => ( +
= 500 ? styles.over500 : undefined}> +
名前:{item.name}
+
{item.price}円
+
+ ))} +
+
+ + + +
+
+ ) +}