アプリを作ってみよう
商品リストを作ってみる
みなさんにはこの節の最後に Todo リストを作ってもらうのですが、商品リストをテーマに、Todo リストに必要な機能をピックアップしていきます。
こんな感じのを作っていきます。
必要な要素を考える
上の gif のようなアプリを実現するためには何が必要か考えてみましょう。
- 商品リストのコンポーネントを作る
- 商品のリストデータを保存する
- 商品のリストデータを表示する
- 商品を追加できる
- 商品の値段が 500 円以上だったら赤くする
- 商品の値段が 1000 円以上だったら「高額商品」と表示する
こんな感じでしょうか。
それでは上から順番に実装していきましょう。
商品リストのコンポーネントを作る
components
ディレクトリにItemList.tsx
というファイルを作成します。
src/components/ItemList.tsx
中身はコンポーネントに最低限必要な部分だけ書きます。
export default function ItemList() {
return <div>ItemList</div>
}
App.tsx
import ClickCounter from './components/ClickCounter'
import ItemList from './components/ItemList'
function App() {
return (
<div>
<ClickCounter />
<ItemList />
</div>
)
}
export default App
表示されました。 こうすることで、後はItemList.tsx
の中身を書き変えればよくなります。
商品のリストデータを保存する
商品リストのデータを保存するのに適当な変数の型は何でしょうか?
商品「リスト」なので配列がよさそうです。
というわけで、配列を使ってデータを保持することにします。
今は商品の追加ができないので、とりあえずダミーデータを入れておきます。
参考
Array | MDN
JavaScript オブジェクトの基本 - ウェブ開発を学ぶ | MDN
import { useState } from 'react'
interface Item {
name: string
price: number
}
export default function ItemList() {
const [items, setItems] = useState<Item[]>([
{ name: 'たまご', price: 100 },
{ name: 'りんご', price: 160 }
])
return <div>ItemList</div>
}
4~7 行目は TypeScript の記法で、Item
という型をinterface
を用いて定義しています。
そして useState
のジェネリクスにItem[]
を渡すことで、items
変数をItem
型の配列として扱えるようにしています。
参考: インターフェース (interface) | TypeScript 入門『サバイバル TypeScript』
商品のリストデータを表示する
先ほど定義したリストの情報を表示していきます。
React ではリストデータを for 文のようにループさせて表示させるには、map
を使います。map
を使うときにはkey
を設定しなければいけません(理由(やや難): Rendering Lists – React)。
これを使ってデータを表示してみます。
import { useState } from 'react'
interface Item {
name: string
price: number
}
export default function ItemList() {
const [items, setItems] = useState<Item[]>([
{ name: 'たまご', price: 100 },
{ name: 'りんご', price: 160 }
])
return (
<div>
<div>ItemList</div>
<div>
{items.map((item) => (
<div key={item.name}>
<div className="name">名前:{item.name}</div>
<div className="price">{item.price}円</div>
</div>
))}
</div>
</div>
)
}
表示できました。
商品を追加する
フォームを使って、入力された文字を変数に格納します。
参考: input – React
これを使って商品を追加できるようにしてみます。
import { useState } from 'react'
interface Item {
name: string
price: number
}
export default function ItemList() {
const [items, setItems] = useState<Item[]>([
{ name: 'たまご', price: 100 },
{ name: 'りんご', price: 160 }
])
const [newItemName, setNewItemName] = useState('')
const [newItemPrice, setNewItemPrice] = useState(0)
const addItem = () => {
setItems([...items, { name: newItemName, price: newItemPrice }])
}
return (
<div>
<div>ItemList</div>
<div>
{items.map((item) => (
<div key={item.name}>
<div className="name">名前:{item.name}</div>
<div className="price">{item.price}円</div>
</div>
))}
</div>
<div>
<label>
名前
<input onChange={(e) => setNewItemName(e.target.value)} type="text" value={newItemName} />
</label>
<label>
価格
<input
onChange={(e) => setNewItemPrice(Number(e.target.value))}
type="number"
value={newItemPrice}
/>
</label>
<button onClick={addItem}>add</button>
</div>
</div>
)
}
参考: アロー関数式 | MDN
できました!
練習問題 1:商品リストに機能を追加
このままだとボタンを連打して商品の追加ができてしまいます。
- ボタンを押したら入力欄を空にする機能
- 入力欄が空だったらボタンを押しても追加されないようにする機能
を追加してみましょう。
商品の値段が 500 円以上だったら赤くする
条件が満たされたときだけ CSS を当てるようにしてみます。items
配列を展開するところで。三項演算子を用いて条件を満たしたときにだけclassName
がつくようにします。
参考: CSS の基本 | MDN
参考: 条件 (三項) 演算子 - JavaScript | MDN
参考: css-modules/css-modules: Documentation about css-modules
import { useState } from 'react'
import styles from './ItemList.module.css'
interface Item {
name: string
price: number
}
export default function ItemList() {
const [items, setItems] = useState<Item[]>([
{ name: 'たまご', price: 100 },
{ name: 'りんご', price: 160 }
])
const [newItemName, setNewItemName] = useState('')
const [newItemPrice, setNewItemPrice] = useState(0)
const addItem = () => {
setItems([...items, { name: newItemName, price: newItemPrice }])
}
return (
<div>
<div>ItemList</div>
<div>
{items.map((item) => (
<div key={item.name} className={item.price >= 500 ? styles.over500 : undefined}>
<div className="name">名前:{item.name}</div>
<div className="price">{item.price}円</div>
</div>
))}
</div>
<div>
<label>
名前
<input onChange={(e) => setNewItemName(e.target.value)} type="text" value={newItemName} />
</label>
<label>
価格
<input
onChange={(e) => setNewItemPrice(parseInt(e.target.value))}
type="number"
value={newItemPrice}
/>
</label>
<button onClick={addItem}>add</button>
</div>
</div>
)
}
そして、src/components/ItemList.module.css
を作成して以下の内容を記入します。
.over500 {
color: red;
}
商品の値段が 10000 円以上だったら「高額商品」と表示する
ある特定の条件を満たした場合のみ、対象コンポーネントを表示するという機能を&&
を使って実現します。もちろん、三項演算子を使っても実現できます。
参考: Conditional Rendering – React
これを使って商品の値段が 10000 円以上だったら「高額商品」と表示するという機能を実現してみましょう。
import { useState } from 'react'
import styles from './ItemList.module.css'
interface Item {
name: string
price: number
}
export default function ItemList() {
const [items, setItems] = useState<Item[]>([
{ name: 'たまご', price: 100 },
{ name: 'りんご', price: 160 }
])
const [newItemName, setNewItemName] = useState('')
const [newItemPrice, setNewItemPrice] = useState(0)
const addItem = () => {
setItems([...items, { name: newItemName, price: newItemPrice }])
}
return (
<div>
<div>ItemList</div>
<div>
{items.map((item) => (
<div key={item.name} className={item.price >= 500 ? styles.over500 : undefined}>
<div className="name">名前:{item.name}</div>
<div className="price">{item.price}円</div>
{item.price >= 10000 && <div>高額商品</div>}
</div>
))}
</div>
<div>
<label>
名前
<input onChange={(e) => setNewItemName(e.target.value)} type="text" value={newItemName} />
</label>
<label>
価格
<input
onChange={(e) => setNewItemPrice(parseInt(e.target.value))}
type="number"
value={newItemPrice}
/>
</label>
<button onClick={addItem}>add</button>
</div>
</div>
)
}
これで商品リストが完成しました!
Todo リストを作る
ここまで紹介してきた機能を使うことで Todo リストが作れるはずです。 頑張りましょう!
練習問題 2:Todo リストを作る
Todo リストを作りましょう。
必要な機能は以下の通りです。
- タスクは未完または完了済みの状態を持つ。
- タスクはタスク名を持つ。
- 未完タスクのリストと完了済みタスクのリストが表示される。
- タスクを完了させることができる。
- タスクの追加ができる。
以上の機能が実現されていれば後は自由です。 スタイルが気になる人は CSS なども書きましょう。
制作物を公開する
Vue の Todo リストを公開したときと同様の方法で公開できるはずです。やってみましょう。