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>
);
};
CodePudding user response:
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>
);
}
CodePudding user response:
It could be timing issues between executing the setInterval and actual mounting of the component (<Analog />) itself. You should use componentDidMount in case of class components or you can use useEffect in case of functional component .
NOTE: Try not to bypass React in order to access DOM (like document.querySelector()).
CodePudding user response:
Is there any other element using second-hand, I assume not? At each render, your function will run before the element is returned, hence your secondHand is undefined.
