This is a trivial analog clock that seems simple enough. However, it seems to fail at line 7 with "TypeError: Cannot read properties of null (reading ‘style’)". I’ve also tried the setAttribute() method and get the same thing. How do I dynamically update the rotation every second?
export const Analog = () => {
function setDate() {
let secondHand = document.querySelector(".second-hand");
const now = new Date();
const seconds = now.getSeconds();
const secondsDegrees = (seconds / 60) * 360 + 90;
secondHand.style.transform = `rotate(${secondsDegrees}deg)`;
}
setInterval(setDate, 1000);
return (
<svg id="clock" className="min-w-full min-h-full" viewBox="0 0 100 100">
<circle id="face" cx="50" cy="50" r="50" />
<g id="hands">
<circle id="center" cx="50" cy="50" r="2" fill="white" />
<line
id="secondHand"
className="second-hand"
x1="50"
y1="50"
x2="50"
y2="0"
/>
</g>
</svg>
);
};
>Solution :
In react you should almost never need to do direct dom manipulation, and definitely not for the scenario you have here. Instead, you should use a state variable to track how much it’s supposed to be rotated, and use that variable in the render output. Additionally, you don’t want to set an interval in the body of your component, as that will result in an ever increasing number of intervals. useEffect should be used instead.
export const Analog = () => {
const [secondsDegrees, setSecondsDegrees] = useState(0);
useEffect(() => {
const id = setInterval(() => {
const now = new Date();
const seconds = now.getSeconds();
setSecondsDegrees((seconds / 60) * 360 + 90);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<svg id="clock" className="min-w-full min-h-full" viewBox="0 0 100 100">
<circle id="face" cx="50" cy="50" r="50" />
<g id="hands">
<circle id="center" cx="50" cy="50" r="2" fill="white" />
<line
id="secondHand"
className="second-hand"
style={{ transform: `rotate(${secondsDegrees}deg)` }}
x1="50"
y1="50"
x2="50"
y2="0"
/>
</g>
</svg>
);
}