Skip to content

アプリを作ってみよう

商品リストを作ってみる

みなさんにはこの節の最後に Todo リストを作ってもらうのですが、商品リストをテーマに、Todo リストに必要な機能をピックアップしていきます。

こんな感じのを作っていきます。

必要な要素を考える

上の gif のようなアプリを実現するためには何が必要か考えてみましょう。

  • 商品リストのコンポーネントを作る
  • 商品のリストデータを保存する
  • 商品のリストデータを表示する
  • 商品を追加できる
  • 商品の値段が 500 円以上だったら赤くする
  • 商品の値段が 1000 円以上だったら「高額商品」と表示する

こんな感じでしょうか。
それでは上から順番に実装していきましょう。

商品リストのコンポーネントを作る

componentsディレクトリにItemList.tsxというファイルを作成します。

src/components/ItemList.tsx

中身はコンポーネントに最低限必要な部分だけ書きます。

tsx
export default function ItemList() {
  return <div>ItemList</div>
}

App.tsx

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

tsx
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)。

参考: Rendering Lists – React

これを使ってデータを表示してみます。

tsx
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

これを使って商品を追加できるようにしてみます。

tsx
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

tsx
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を作成して以下の内容を記入します。

tsx
.over500 {
  color: red;
}

商品の値段が 10000 円以上だったら「高額商品」と表示する

ある特定の条件を満たした場合のみ、対象コンポーネントを表示するという機能を&&を使って実現します。もちろん、三項演算子を使っても実現できます。

参考: Conditional Rendering – React

これを使って商品の値段が 10000 円以上だったら「高額商品」と表示するという機能を実現してみましょう。

tsx
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 リストを公開したときと同様の方法で公開できるはずです。やってみましょう。