【React + TypeScript】Switch文を使って、動的に色を変える

## はじめに

この度、「RailsAPIのレスポンスの内容によって、画面の色を変化させる」という実装を行いました。その結果を、アウトプットとしてまとめました。

## 記事の内容

- React + TypeScriptで色を動的に変化させることができるようになる

## 準備

- 今回は、ChakraUIを使用しました。

```
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
```

[Getting Started](https://chakra-ui.com/docs/getting-started)



## 今回使用するサンプルプログラム

```jsx:App.js
import { ChakraProvider, Text, Center, Button, Box } from "@chakra-ui/react";
import React, { useState } from "react";

type FoodType = {
  name: string;
  value: number;
};

export default function App() {
  //オブジェクトの型定義が曖昧です。
  const [food, setFood] = useState<FoodType>({ name: "初期値", value: 0 });

  const foods = [
    { name: "りんご", value: 300 },
    { name: "ぶどう", value: 1000 },
    { name: "バナナ", value: 200 }
  ];

  const onClickButton = () => {
    const newFood = foods[Math.floor(Math.random() * foods.length)];
    setFood(newFood);
  }

  return (
    <>
      <ChakraProvider>
        <Box bg="black" m={4}>
          <Center>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              名前: {food.name}
            </Text>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              価格: {food.value}円
            </Text>
          </Center>
        </Box>
        <Center>
          <Button onClick={onClickButton}>ボタン</Button>
        </Center>
      </ChakraProvider>
    </>
  );
}
```

ボタンをクリックすると、ランダムに取得したfoodに更新されるプログラムを作成しました。



<img width="840" alt="スクリーンショット 2021-09-24 16 58 13" src="https://user-images.githubusercontent.com/64491435/134652160-808b23e1-25c7-438d-af81-8a96e3c20966.png">

この状態から、foodsのnameによって、背景色が変化するプログラムを追加します。


## 実装

1.色を取得する関数を作成
ここで、タイトルにある通り、switch文の条件によって、色を取得しています。
例えば、`food.name`が"りんご"の場合、`red.400`が背景色になります。

```jsx:App.js
const getFoodColor = useCallback(
    (food: FoodType) => {
      switch (food.name) {
        case "りんご":
          return "red.400";
        case "ぶどう":
          return "blue.400";
        case "みかん":
          return "orange.400";
        case "バナナ":
          return "yellow.300";
        default:
          return "black.400";
      }
    },
    []
  );
```

2.色を取得し、変数に格納

先程作成した関数(`getFoodColor`)から、`food`の値が変わった場合に、色を取得します。
また、メモ化して無駄な再レンダリングを制限しています。(foodの値が変更された場合のみ更新される)

```jsx:App.js
const foodColor = useMemo(() => getFoodColor(food), [food])
```

３．取得した色を背景に設定
最後に、取得した色を背景として設定します。

```jsx:App.js
//before
<Box bg="black" m={4}>

//after
<Box bg={foodColor} m={4}>
```

- 全体像

```jsx:App.js
import { ChakraProvider, Text, Center, Button, Box } from "@chakra-ui/react";
import React, { useCallback, useMemo, useState } from "react";

type FoodType = {
  name: string;
  value: number;
};

export default function App() {
  const [food, setFood] = useState<FoodType>({ name: "初期値", value: 0 });

  const foods = [
    { name: "りんご", value: 300 },
    { name: "ぶどう", value: 1000 },
    { name: "バナナ", value: 200 }
  ];

  const getFoodColor = useCallback((food: FoodType) => {
    switch (food.name) {
      case "りんご":
        return "red.400";
      case "ぶどう":
        return "blue.400";
      case "みかん":
        return "orange.400";
      case "バナナ":
        return "yellow.300";
      default:
        return "black.400";
    }
  }, []);

  const foodColor = useMemo(() => getFoodColor(food), [food]);

  const onClickButton = () => {
    const newFood = foods[Math.floor(Math.random() * foods.length)];
    setFood(newFood);
  };

  return (
    <>
      <ChakraProvider>
        <Box bg={foodColor} m={4}>
          <Center>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              名前: {food.name}
            </Text>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              価格: {food.value}円
            </Text>
          </Center>
        </Box>
        <Center>
          <Button onClick={onClickButton}>ボタン</Button>
        </Center>
      </ChakraProvider>
    </>
  );
}
```

- ボタンをクリックすると、色がランダムに取得でき、出力されるプログラムが完成しました。


<img width="840" alt="りんご" src="https://user-images.githubusercontent.com/64491435/134651801-1a133b18-376a-4a20-affc-fbf92f32a8f1.png">

<img width="840" alt="ぶどう" src="https://user-images.githubusercontent.com/64491435/134651798-4a573cad-1901-4b0e-a86e-3010980c8406.png">

<img width="840" alt="バナナ" src="https://user-images.githubusercontent.com/64491435/134651792-451a9f11-751c-4607-8578-4466272c7961.png">


## カスタムフック化

- カスタムフック化をしておきます。

```jsx:App.js
import { ChakraProvider, Text, Center, Button, Box } from "@chakra-ui/react";
import React, { useMemo, useState } from "react";

import { useFoodColor } from "./useFoodColor";
import { FoodType } from "../type/food";

export default function App() {
  const [food, setFood] = useState<FoodType>({ name: "初期値", value: 0 });

  const foods = [
    { name: "りんご", value: 300 },
    { name: "ぶどう", value: 1000 },
    { name: "バナナ", value: 200 }
  ];

  const { getFoodColor } = useFoodColor();
  const foodColor = useMemo(() => getFoodColor(food), [food]);

  const onClickButton = () => {
    const newFood = foods[Math.floor(Math.random() * foods.length)];
    setFood(newFood);
  };

  return (
    <>
      <ChakraProvider>
        <Box bg={foodColor} m={4}>
          <Center>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              名前: {food.name}
            </Text>
            <Text color="white" fontSize="2xl" w="2xl" textAlign="center">
              価格: {food.value}円
            </Text>
          </Center>
        </Box>
        <Center>
          <Button onClick={onClickButton}>ボタン</Button>
        </Center>
      </ChakraProvider>
    </>
  );
}
```

```jsx:useFoodColor.ts
import { useCallback } from "react";
import { FoodType } from "../type/food";

export const useFoodColor = () => {

  const getFoodColor = useCallback((food: FoodType) => {
    switch (food.name) {
      case "りんご":
        return "red.400";
      case "ぶどう":
        return "blue.400";
      case "みかん":
        return "orange.400";
      case "バナナ":
        return "yellow.300";
      default:
        return "black.400";
    }
  }, []);

  return { getFoodColor };
};
```

型定義も別で切り出しました。

```jsx:src/type/food.ts
export type FoodType = {
  name: string;
  value: number;
};
```

以上になります。

## 参考資料

[JavaScript | 配列・多次元配列からランダムに値を取得する方法 | ONE NOTES](https://1-notes.com/javascript-get-values-randomly-from-an-array/)