0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【オリジナルアプリ.2】購入履歴管理アプリRecapを作る!

Last updated at Posted at 2026-01-30

理解を深めるためのメモ兼、思い出すため用です。

作成したもの

今回作成するオリジナルアプリの見た目の作成、ヘッダーのフィルターボタンの追加を行いました。
スクリーンショット 2026-01-30 19.07.47.png

見た目の作成

App.jsx:ルートの設定

App.jsx
function App() {
  return (
    <Router>
      <Box pb="60px">
        {" "}
        {/* 下のメニューが被らないように余白を作る */}
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/analysis" element={<Analysis />} />
          <Route path="/calendar" element={<Calendar />} />
        </Routes>
      </Box>
      <Navbar />
    </Router>
  );
}

Navbar.jsx:画面下のボタン(今は必要最低限、css部分はAI)
スクリーンショット 2026-01-30 19.13.55.png

Navbar.jsx
const Navbar = () => {
  return (
    <Flex
      as="nav"
      fixed="bottom"
      position="fixed"
      bottom="0"
      width="100%"
      bg="white"
      borderTop="1px solid #eee"
      justify="space-around"
      py="5"
    >
      <Link to="/">
        <Text fontSize="ms">一覧</Text>
      </Link>
      <Link to="/analysis">
        <Text fontSize="ms">分析</Text>
      </Link>
      <Link to="/calendar">
        <Text fontSize="ms">カレンダー</Text>
      </Link>
    </Flex>
  );
};

Home.jsx:ホーム画面全体(HeaderとCardCompを扱ってる)
→ここが今回の肝。フィルター機能。これについては後ほど記述。見た目はreturn以降の部分
スクリーンショット 2026-01-30 19.18.11.png

Home.jsx
const Home = () => {
  // ダミーデータ
  const data = [
    { id: 1, name: "ワイヤレスイヤホン", price: "5,800", category: "Amazon", date: "2026/01/29" },
    { id: 2, name: "冬物ニット", price: "3,200", category: "SHEIN", date: "2026/01/28" },
    { id: 3, name: "スマホケース", price: "1,500", category: "Amazon", date: "2026/01/27" },
  ];
  // 「現在選択中のカテゴリ」を管理するState(初期値は "全て")
  const [selectedCategory, setSelectedCategory] = useState("全て");

  // カテゴリだけを取ってきて配列で管理
  const categories = ["全て", ...new Set(data.map((item) => item.category))];

  // 表示するデータを選定するところ
  // 選択中のカテゴリが「全て」ならdataを、それ以外ならdataの中のカテゴリと一致するものだけ取り出す
  const filteredData =
    selectedCategory === "全て"
      ? data
      : data.filter((item) => item.category === selectedCategory);

  return (
    <Box minH="100vh" bg="gray.50">
      {/* Headerに、現在の選択状態と、カテゴリ配列、「今、どのカテゴリが選ばれているか」を書き換えるための専用リモコンを渡す */}
      <Header
        categories={categories}
        selectedCategory={selectedCategory}
        setSelectedCategory={setSelectedCategory}
      />

      <Box p={4} pb="100px">
        {/* 選定されたデータの中身を1つずつ取り出してCardCompに渡す */}
        {filteredData.map((item) => (
          <CardComp
            key={item.id}
            name={item.name}
            price={item.price}
            category={item.category}
            date={item.date}
          />
        ))}
      </Box>

      {/*「+」ボタン */}
      <IconButton
        icon={<AddIcon />}
        colorScheme="orange"
        isRound
        size="lg"
        position="fixed"
        bottom="100px"
        right="20px"
        shadow="2xl"
        onClick={() => alert("追加機能はこれから実装!")}
      />
    </Box>
  );
};

Header.jsx
スクリーンショット 2026-01-30 19.24.31.png

Header.jsx
const Header = ({ categories, selectedCategory, setSelectedCategory }) => {
  return (
    <VStack
      p={4}
      borderBottom="1px solid #eee"
      spacing={3}
      bg="white"
      position="sticky"
      top="0"
      zIndex="10"
    >
      <Text fontSize="xl" fontWeight="bold">
        Recap
      </Text>
      <HStack spacing={2} overflowX="auto" width="100%" py={1}>
        {/* 受け取ったカテゴリ一覧から1つずつ取り出す */}
        {categories.map((cat) => (
          <Button
            key={cat}
            size="sm"
            borderRadius="full"
            // 選ばれているカテゴリなら色を塗りつぶし(solid)、そうでなければ枠線(outline)
            variant={selectedCategory === cat ? "solid" : "outline"}
            colorScheme="orange"
            // クリックされたら、HomeのselectedCategoryを更新する
            onClick={() => setSelectedCategory(cat)}
          >
            {cat}
          </Button>
        ))}
      </HStack>
    </VStack>
  );
};

CardComp.jsx
スクリーンショット 2026-01-30 19.52.11.png

CardComp.jsx
const CardComp = ({ name, price, category, date }) => {
  return (
    <Box p={4}>
      <Box bg="orange.100" p={5} borderRadius="lg" shadow="md" mt={4}>
        <Flex align="center">
          <VStack align="start" spacing={0}>
            <Text fontWeight="bold">{name}</Text>
            <Text fontSize="sm" color="gray.600">
              {category}
            </Text>
          </VStack>

          <Spacer />

          <VStack align="end" spacing={0}>
            <Text fontWeight="bold">¥{price}</Text>
            <Text fontSize="xs">{date}</Text>
          </VStack>
        </Flex>
      </Box>
    </Box>
  );
};

フィルタリング機能

やりたいこと

  1. 画像のようにカテゴリごとにヘッダーにボタンを作成
  2. ボタンを押したら、そのカテゴリの履歴だけを表示する
    スクリーンショット 2026-01-30 19.24.31.png

1.カテゴリごとにヘッダーにボタンを作成

Home.jsx
  // カテゴリだけを取ってきて配列で管理
  const categories = ["全て", ...new Set(data.map((item) => item.category))];

Setは、重複する値は格納しない。
これを活用して、ダミーデータdataをマップしてカテゴリを1つずつみていけば、重複せずにカテゴリを全て取り出すことができる。

あとは、このカテゴリの配列をHeaderコンポーネントに渡してあげれば良い。

Header.jsx
      <HStack spacing={2} overflowX="auto" width="100%" py={1}>
        {/* 受け取ったカテゴリ一覧から1つずつ取り出す */}
        {categories.map((cat) => (
          <Button>{cat}</Button>
        ))}
      </HStack>

受け取ったカテゴリ一覧をmapで1つずつみて、Buttonをそれぞれ生成すればOK。

2.ボタンを押したら、そのカテゴリの履歴だけを表示する(フィルタリング)

Home.jsx
  // 「現在選択中のカテゴリ」を管理するState(初期値は "全て")
  const [selectedCategory, setSelectedCategory] = useState("全て");

  // 表示するデータを選定するところ
  const filteredData =
    selectedCategory === "全て"
      ? data
      : data.filter((item) => item.category === selectedCategory);
  1. まずは、Home.jsxに選択されているカテゴリを管理するStateを用意
  2. filteredData:選択中のカテゴリが「全て」ならdataを、それ以外ならdataの中のカテゴリと一致するものだけ取り出す。(selectedCategoryがAmazonなら、データの中のcategoryがAmazonのものだけ取り出す。)
Home.jsx
        {filteredData.map((item) => (
          <CardComp
            key={item.id}
            name={item.name}
            price={item.price}
            category={item.category}
            date={item.date}
          />
        ))}

そして、CardCompにはfilteredDataでフィルタリングしたデータを渡す。すると自動でフィルタリングされる!

Header.jsx
      <HStack spacing={2} overflowX="auto" width="100%" py={1}>
        {/* 受け取ったカテゴリ一覧から1つずつ取り出す */}
        {categories.map((cat) => (
          <Button
            key={cat}
            size="sm"
            borderRadius="full"
            // 選ばれているカテゴリなら色を塗りつぶし(solid)、そうでなければ枠線(outline)
            variant={selectedCategory === cat ? "solid" : "outline"}
            colorScheme="orange"
            // クリックされたら、HomeのselectedCategoryを更新する
            onClick={() => setSelectedCategory(cat)}
          >
            {cat}
          </Button>
        ))}
      </HStack>

そして、

  • variant={selectedCategory === cat ? "solid" : "outline"}で選択されているボタンの見た目を変える。
  • onClick={() => setSelectedCategory(cat)}でクリックされたら選択中Stateを変更する。

スクリーンショット 2026-01-30 19.56.17.png

以上です!ご覧頂きありがとうございました!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?