1

I am trying to add the classname each second and remove it within that second ( continuously like this). I am using a useEffect hook for it .

function App() {
  const [date , setDate] = useState('')
  let circle_boolean = 0;

  useEffect(() => {
    const interval = setInterval(() => {
      doThings()
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  function doThings() {
    setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
    circle_boolean=1;
    console.log("cirlce boolean :" + circle_boolean)
  }

  console.log("cirlce boolean outside:" + circle_boolean)


  return (
    <div className="App">
      <div className="bg">
        <div className={circle_boolean?"circle":""}/>  
        <div className="card">
          <p className="card-info">{date}</p>
        </div>
      </div>
    </div>
  );
}

I am trying to use a variable "circle_boolean" for that purpose. If circle_boolean is '1' I want the circle class to be added and removed afterwards. How do I do it in the right way?

3 Answers3

2

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.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • this solves it . But why do we does using a variable inside useEffect become stale ? – mahaveer jeevagan Jan 08 '21 at 14:33
  • 1
    @mahaveerjeevagan - I've updated the answer to address that. Also, if you search for "React stale state" you'll find a lot of articles on the subject (of varying quality, of course, as always :-) ). – T.J. Crowder Jan 08 '21 at 15:06
0

The second property of useEffect is an array that helps us to control states on the component. I mean if you want to run a method on state change, you can simply use a useEffect hook with that state to check changes on that state.

function App() {
  const [date , setDate] = useState('')
  const [boolean, setBool] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => {
      doThings()
    }, 1000);
    return () => clearInterval(interval);
  }, []);

  // Magic happens in here:
  useEffect(() => {
    setTimeout(() => { setBool(false); }, 500); // to run after half a sec
  }, [bool]);

  function doThings() {
    setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
    setBool(true);
    console.log("cirlce boolean :" + bool)
  }
    
  return (
    <div className="App">
      <div className="bg">
        <div className={bool?"circle":""}/>  
        <div className="card">
          <p className="card-info">{date}</p>
        </div>
      </div>
    </div>
  );
}

Also another simpler way is using css animation.

Pouya Jabbarisani
  • 1,084
  • 3
  • 16
  • 29
0

I made sure that you use useEffect and pass second argument as state value for circle_boolean( This needs to be in the state and I have renamed it as circleBoolean). The idea is that I make the state toggle and also useEffect() gets called on every second and since the state toggles our circle class get's added and removed as well.

Try this solution:

https://codesandbox.io/s/setting-class-3r54k?file=/src/App.js

FULL CODE:

const {useState, useEffect} = React;

function App() {
  const [date, setDate] = useState("");
  const [circleBoolean, setCircleBoolean] = useState(false);

  useEffect(() => {
    const interval = setInterval(() => {
      setCircleBoolean(!circleBoolean);
      setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
    }, 1000);
    return () => {
      clearInterval(interval);
    };
  }, [circleBoolean]);

  return (
    <div className="App">
      <div className="bg">
        <div className={`${circleBoolean ? "circle" : ""}`}></div>
        <div className="card">
          <p className="card-info">{date}</p>
        </div>
      </div>
    </div>
  );
}

ReactDOM.render(<App/>, document.getElementById("root"));
.App {
  font-family: sans-serif;
  text-align: center;
}

.circle {
  height: 20px;
  width: 20px;
  background: green;
  background: orange;
}
<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>
Imran Rafiq Rather
  • 7,677
  • 1
  • 16
  • 35
  • This works . But why do we have to use **{`${circleBoolean ? "circle" : ""}`}** instead of **{circleBoolean ? "circle" : ""}** ? – mahaveer jeevagan Jan 08 '21 at 14:30
  • Sure @T.J.Crowder : Thanks for bringing this one ...Will use the same approach from now onwards and edit this answer as well.. But Do you see anything wrong with the approach that I have provided for the OP overall :) Thanks – Imran Rafiq Rather Jan 08 '21 at 15:44
  • @mahaveerjeevagan : I am using template strings of ES6, And this is called string interpolation We as Javascript developers have adopted to this as this is very flexible.. In React so often use.. To concatenate, to create HTML Templates and adding dynamic value from variable and for many other uses...This is extremely helpful :) – Imran Rafiq Rather Jan 08 '21 at 15:49
  • It's hard to say from your description. If you're adding the flag to the dependency array on `useEffect`, then I'd say that's not the best way to do it because that constantly unschedules and reschedules the timer. (See [my answer](https://stackoverflow.com/a/65628861/157247) for what I'd do instead.) – T.J. Crowder Jan 08 '21 at 15:58
  • I made sure on every state change my useEffect() gets invoked... That's what I feel is different from your solution :) My Idea is a bit different from yours... And Loads of respect for you Sir :) You have great contributions for the community !!! – Imran Rafiq Rather Jan 08 '21 at 15:59
  • :-) Thanks. Yeah, I realized that and deleted my comment, then posted the one above. You may not have seen the newer one. – T.J. Crowder Jan 08 '21 at 16:02
  • Hmmm , I see ! Would love to improve and master stuff :) with time !!! Thanks for your valuable advises and typical helpful guidance as well :) – Imran Rafiq Rather Jan 08 '21 at 16:02
  • Ya I didn't checked it ...Let me check ? – Imran Rafiq Rather Jan 08 '21 at 16:03
  • Sir, Do you have any advise for me about how I can grow as a Developer and make an impact on society and help people to make their lives better :) Thanks in advance – Imran Rafiq Rather Jan 08 '21 at 16:04
  • 1
    @ImranRafiqRather - I'm afraid I don't, but FWIW, if you keep trying to be helpful as you are here, keep listening (sometimes it involves listening to people who are making you cross, which is difficult but sometimes worth it -- people are sometimes not good at feedback), and never stop learning, you'll do well. :-) – T.J. Crowder Jan 08 '21 at 16:14
  • Thanks a lot for the Valuable advices :) I ask God to help me to stay steadfast and motivated :) Thank you once again for your valuable time as well :) – Imran Rafiq Rather Jan 08 '21 at 17:31