
테두리가 반짝이는 카드가 있다.
상태가 바뀌면 테두리 색이 바뀐다.
변경된 카드와 나머지 카드의 반짝이는 타이밍이 맞지 않다.
이것을 keyframes가 아닌 useState로 반짝임을 관리하도록 수정했다.
App.jsximport React, { useState } from "react";
import SensorCard from "./Sensor";
const data = new Array(10).fill(undefined).map((item, index) => {
return {
name: `sensor${index}`,
serial: `serial${index}`,
status: "normal",
index,
};
});
export default function App() {
const [sensorList, setSensorList] = useState(data);
return (
<div className=' '>
<div className='grid gap-7 md:gap-3 grid-cols-10 3xl:grid-cols-8 xl:grid-cols-9 md:grid-cols-8 sm:grid-cols-4'>
{sensorList.map((item, i) => {
return (
<div
key={i}
className='mb-[25px] col-span-2 2xl:col-span-2 xl:col-span-3 md:col-span-4 '
>
<SensorCard
data={item}
contentFirst
halfCircleIcon
setSensorList={setSensorList}
/>
</div>
);
})}
</div>
</div>
);
}
import styled, { css, keyframes } from "styled-components";
const color = {
alert: "rgb(239 68 68)",
warning: "rgb(249 115 22)",
normal: "rgb(34 197 94)",
inspection: "rgb(168 85 247)",
};
const blinkAnimation = (props) => keyframes`
0%, 100% {
box-shadow: 0 0 0 1px ${color[props.status]};
}
50% {
box-shadow: none;
}
`;
const BoxBlinkStyle = styled.div`
${(status) =>
css`
animation: ${blinkAnimation(status)} 1.2s infinite;
`}
`;
export const borderColor = {
total: "border-gray-500",
normal: "border-green-500",
alert: "border-red-500",
warning: "border-orange-500",
inspection: "border-purple-500",
};
export default function SensorCard({ data, className, setSensorList }) {
const { name, serial, status, index } = data;
return (
<div className={className}>
<BoxBlinkStyle
status={status}
className={`
bg-white dark:bg-white10 py-[25px] px-[25px] pb-[12px] overflow-hidden rounded-10 relative text-[15px] text-theme-gray dak:text-white60 border `}
>
<div className='flex justify-between items-center text-md mb-3'>
<span
className={`text font-semibold text-body dark:text-white60 text-[15px] `}
>
{name}
</span>
<span className='text-body dark:text-white60 cursor-pointer'></span>
</div>
<div className='flex justify-between mb-2'>
<small className='text-gray-500 font-semibold'>{serial}</small>
<div>
<button
type='button'
className='text-xs font-semibold mb-0 cursor-pointer hover:text-red-300'
onClick={() => {
setSensorList((pre) =>
pre.map((item, idx) => {
if (idx === index) {
return {
...item,
status: item.status === "normal" ? "alert" : "normal",
};
}
return item;
})
);
}}
>
변경
</button>
</div>
</div>
</BoxBlinkStyle>
</div>
);
}

import React, { useState, useEffect } from "react";
import styled from "styled-components";
const sensorsData = [
{ id: 1, name: "Sensor 1", status: "normal" },
{ id: 2, name: "Sensor 1", status: "normal" },
{ id: 3, name: "Sensor 1", status: "normal" },
{ id: 4, name: "Sensor 1", status: "normal" },
{ id: 5, name: "Sensor 1", status: "normal" },
{ id: 6, name: "Sensor 1", status: "normal" },
{ id: 7, name: "Sensor 1", status: "normal" },
{ id: 8, name: "Sensor 1", status: "normal" },
{ id: 9, name: "Sensor 1", status: "normal" },
{ id: 10, name: "Sensor 1", status: "normal" },
{ id: 11, name: "Sensor 1", status: "normal" },
{ id: 12, name: "Sensor 1", status: "normal" },
{ id: 13, name: "Sensor 1", status: "normal" },
{ id: 14, name: "Sensor 1", status: "normal" },
{ id: 15, name: "Sensor 1", status: "normal" },
// ... (나머지 센서 데이터)
];
const SensorCard = styled.div`
border: 2px solid
${(props) => {
return props.isBlinking
? props.status === "danger"
? "red"
: "green"
: "transparent";
}};
border-radius: 8px;
padding: 16px;
margin: 10px;
position: relative;
transition: border 1.2s ease;
`;
const ChangeButton = styled.button`
margin-top: 8px;
cursor: pointer;
`;
const App = () => {
const [sensorData, setSensorData] = useState(sensorsData);
const [flashing, setFlashing] = useState(true);
const [isBlinking, setIsBlinking] = useState(false);
useEffect(() => {
const intervalId = setInterval(() => {
setIsBlinking((prevIsBlinking) => !prevIsBlinking);
}, 1200); // 1200ms 간격으로 토글
return () => {
clearInterval(intervalId);
};
}, []);
const handleStatusChange = (id) => {
setSensorData((prevData) =>
prevData.map((sensor) =>
sensor.id === id
? {
...sensor,
status: sensor.status === "danger" ? "normal" : "danger",
}
: sensor
)
);
};
return (
<div>
{sensorData.map((sensor) => (
<SensorCard
key={sensor.id}
status={sensor.status}
isBlinking={isBlinking}
>
<h3>{sensor.name}</h3>
<p>Status: {sensor.status}</p>
<ChangeButton onClick={() => handleStatusChange(sensor.id)}>
Change Status
</ChangeButton>
</SensorCard>
))}
</div>
);
};
export default App;
