はじめに
この度、「RailsAPIのレスポンスの内容によって、画面の色を変化させる」という実装を行いました。その結果を、アウトプットとしてまとめました。
記事の内容
- React + TypeScriptで色を動的に変化させることができるようになる
準備
- 今回は、ChakraUIを使用しました。
npm i @chakra-ui/react @emotion/react@^11 @emotion/styled@^11 framer-motion@^4
今回使用するサンプルプログラム
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に更新されるプログラムを作成しました。
この状態から、foodsのnameによって、背景色が変化するプログラムを追加します。
実装
1.色を取得する関数を作成
ここで、タイトルにある通り、switch文の条件によって、色を取得しています。
例えば、food.name
が"りんご"の場合、red.400
が背景色になります。
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の値が変更された場合のみ更新される)
App.js
const foodColor = useMemo(() => getFoodColor(food), [food])
3.取得した色を背景に設定
最後に、取得した色を背景として設定します。
App.js
//before
<Box bg="black" m={4}>
//after
<Box bg={foodColor} m={4}>
- 全体像
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>
</>
);
}
- ボタンをクリックすると、色がランダムに取得でき、出力されるプログラムが完成しました。
カスタムフック化
- カスタムフック化をしておきます。
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>
</>
);
}
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 };
};
型定義も別で切り出しました。
src/type/food.ts
export type FoodType = {
name: string;
value: number;
};
以上になります。