Since the flag is part of your state, you need to put it in state. I'd also create doThings
in the useEffect
callback since you only want the first one, not the ones created by subsequent calls to your component function.
See ***
comments:
const {useState, useEffect} = React;
function App() {
const [date , setDate] = useState("");
// *** Put the flag in state
const [circleBoolean, setCircleBoolean] = useState(false);
useEffect(() => {
const interval = setInterval(() => {
// *** Note that it's important not to use `date` or
// `circleBoolean` here, they'll be stale. It's okay
// to use the setter functions, though.
setDate(moment().format("MMMM Do YYYY, h:mm:ss a"));
// *** Use the callback form of `setCircleBoolean`
// so we use an up-to-date version of the flag, not
// the stale one
setCircleBoolean(b => !b);
}, 1000);
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<div className="bg">
<div className={circleBoolean?"circle":""}/>
<div className="card">
<p className="card-info">{date}</p>
</div>
</div>
</div>
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
.circle {
background-color: green;
width: 10px;
height: 10px;
border-radius: 10px;
}
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js"></script>
In a comment you've asked:
But why do we does using a variable inside useEffect become stale ?
When you create a function within a function, it closes over the environment in which it was created (the in-scope variables for the function call etc., including locals). More in the answers to this question and (using slightly dated terminology) in my blog post here.
Your component function is called when your component is first being mounted, and then again repeatedly as its state and props change. Every time it's called, a new environment for that call is created, and new locals are created in that environment. But your effect callback is only ever called once, because of the []
dependency array. So the variables it closes over are the ones from the very first call to your component function. That would be just fine if it did all its work right away, but it doesn't: It creates another function and sets it up to be called at timed intervals — your setInterval
callback. Since that timer callback is created during the call to the effect callback, which uses the environment from the first call to your component function, all that timer ever sees is the locals from the first component function call, not subsequent ones. So the state information it closes over becomes stale over time.
That's fine for the above, because we don't use the state information directly. For setDate
, we just set entirely new state based on the time of day. For setCircleBoolean
, we use the callback form of the state setter that passes us the most up-to-date version of the state information (b
) and we use that to update the flag. (And we can use those setter functions because React promises us that they never change during the lifetime of the component.)