Use cases are completed in devlp-env but not when running-tests

You manupulating dom in your component return section but also you using document class methods, why?

          setTimeout(() => document.getElementById("time-left").classList.remove("activated"), 1000);
          SetSessionTime(SessionTime-1);
        }
      }
    },1000);

    return () => clearInterval(timer);
  });

  const ManageSession = (type) => {
    let countdown = document.getElementById("time-left");
    
    if(SessionState==="stopped") {
      countdown.classList.add("activated");
    } else {

      if(SessionType!=="break") {

        if(!SessionOnPause) {
          countdown.classList.add("postponed");
        } else {
          (type==="reset") ? countdown.classList.add("stopped") : countdown.classList.add("activated");
        }
      }
    }
...

Don’t use document class methods in react. Don’t reach your DOM elements with by ID. You can directly manupulating on the return already;

...
 <section className="timer">
        <div className="container">
          <h3 id="timer-label">{(ses_on_countdown) ? "Session" : "Break"}</h3>
          <p id="time-left" 
            className={(ses_running) ? ((ses_on_countdown) ? "active activated" : "on_break") : "deactivated"}>
              {minutes < 10 ? '0'+minutes : minutes}:{seconds < 10 ? '0'+seconds : seconds}
          </p>
        </div>
...

and you can use useRef for manupulate without re-render or control to audio element.

Also, can you explain what error you encountered, what you did to solve it, and how your code works? How do you manage stopping the interval and changing the session type when the time reaches zero? I see there might be an issue, but I want you to find the problem yourself.

One more piece of advice: state names or function names should always use camelCase starting with a lowercase letter. For example, it should be sessionTime or setSessionTime. Commonly components or class names start with an uppercase letter.

@0x74h51N ;

You manupulating dom in your component return section but also you using document class methods, why?

  • the purpose of using the class methods in there is just for removing a specific css-class after a round start up, let me explain: if u click on the play-btn, u can see a css-class named ‘activated’ (makes the #time-left background green) and another css-class named ‘active’ (changes the font color to yellow) will be added & within the end of that running cycle (first iteration of the setInterval() method) that ‘activated’ css-class will be removed (#time-left background color will be reverted). similar scenario will occur when the ‘pause’ action and the ‘reset’ action performed. different color schemas will be change according to the action. [ is this causing a problem? ]

Don’t use document class methods in react. Don’t reach your DOM elements with by ID. You can directly manupulating on the return already;

  • yes I understood wt u said, but the purpose of that class toggle is to be more dynamic based on the action performed when an iteration runs. the static style set css-classes are toggled via automatically based on the state within the DOM element returning. those aren’t change based on the btn-interactions. [ is this causing a problem? ]

and you can use useRef for manupulate without re-render…

  • hv no idea abt this hook, noted & will work on that.

Also, can you explain what error you encountered, what you did to solve it, and how your code works?

  • hv no idea how to solve these because when I do testing myself locally, I dont capture these errors. from rendering context to playing audio doing just fine as the FCC demonstrated project. currently stuck with following errors;


[ ss imgs above are from a prv reply ]

How do you manage stopping the interval and changing the session type when the time reaches zero? I see there might be an issue, but I want you to find the problem yourself.

  • within the useEffect itself the interval is cleared itself in one cycle ends. when the new seconds integer value equals 0 the audio will played & after 1 second the state updated. else, if the seconds value not 0 yet the state update as usual within the else statement inside the useEffect.
  • I’m trying but I can’t pin-point an issue here. :sweat_smile: the '#time-left has not become 00:00 error also couldn’t capture because before converting the structure from the class components to function components, my logic was same exact as now.

One more piece of advice: state names or function names should always use camelCase…

  • yes this was a silly mistake, I knew this but somehow I unintentionally done it. let me fix it.

Of course it cause a problem. My eyes are bleeding :see_no_evil:… Okay just kidding :sweat_smile:
It cause ‘State Synchronization’ or performance problems. Because; React is built around a declarative programming model, where the UI is described as a function of the application state. When you directly manipulate the DOM using methods like document.getElementById , you bypass React’s rendering lifecycle, potentially causing discrepancies between the actual DOM and React’s virtual DOM.

You can change styles using Style attribute on your elements or you can change class as like you did before with states;

...
`className={(ses_running) ? ((ses_on_countdown) ? "active activated" : "on_break") : "deactivated"}>`

Quick answer for this: Ensure you check if the timer has reached zero before the timeInterval ends and don’t wait for the state to update. You can achieve this with a callback function on setState . Within the callback, you can check if prevState is greater than zero and, if so, subtract one from it. Additionally, you need to clear your interval once the session or break time finishes. Make sure to do this each time the timer is change.

The first issue is related to performance and delayed response. The second issue concerns how you update your session time states. If you check the state before updating, as I mentioned, you won’t go below zero.

For your time format, first:

  const [SessionTime,setSessionTime] = React.useState(1500);
  const [SessionBreak,setSessionBreak] = React.useState(5);
  const [SessionLength,setSessionLength] = React.useState(25);

Store your session and break times as seconds, similar to your SessionTime state. Also, you don’t need three states for this; just breakLength and sessionLength would be sufficient. After setting the states, simply format them only for display. For time updates via buttons:

onClick={() => setValue("down",props._type)}>

simplify it, directly adjust the related time for example like this: setTime(timeLength + 60) or setTime(timeLength - 60) . For limits, you can use Math.max or Math.min :wink:. Additionally, you can pass states as props to child components. Consider this for your TimeSet component.

After making these adjustments, I believe you will pass all tests or identify the root of the problem.

1 Like

I was able to pass all the test after making the necessary changes u mentioned above. I’ve updated my CODEPEN as well. You can check & confirm: link. (My local hosted prj & my updated CODEPEN prj passed all 29 tests)


Step 01

You manupulating dom in your component return section but also you using document class methods…

  • noted wt u explained ( ur explanation: It cause ‘State Synchronization’ or performance problems. Because; React is built around a declarative programming model… ) & commented out all my CSS-class toggles within the methods.

Step 02

Ensure you check if the timer has reached zero before the timeInterval ends and don’t wait for the state to update. You can achieve this with a callback function on setState . Within the callback, you can check if prevState is greater than zero and, if so, subtract one from it. Additionally, you need to clear your interval once the session or break time finishes. Make sure to do this each time the timer is change…

  • based on this, I restructured my existing useEffect + ManageSession logic,
  React.useEffect(() => {
    
    const timer = setInterval(() => {

      if(SessionState==="running" && !SessionOnPause) {

        if(SessionTime-1===0) { ///////// newly integrated condition /////////
          document.getElementById("beep").play();
        }

        if(SessionTime===0) {
          clearInterval(timer);

          setTimeout(() => ManageSession("change"),1000);  ///////// newly integrated timeout + method call /////////
        } else {
          /* setTimeout(() => document.getElementById("time-left").classList.remove("activated"), 1000); */
          setSessionTime(SessionTime-1);
        }
      }
    },1000);

    return () => clearInterval(timer);
  });
  const ManageSession = (type) => {
    ///////// commented-out css-class toggles - multiple lines goes here //////////

    if(type==="reset") {
      document.getElementById("beep").pause();
      document.getElementById("beep").currentTime = 0;
      
      setSessionOnPause(false);
      setSessionState("stopped");
      setSessionType("countdown");
      setSessionBreak(300);
      setSessionLength(1500);
      setSessionTime(1500);
    } else if(type==="pause") {
      (SessionType!=="break") && setSessionOnPause(!SessionOnPause);

    } else if(type==="change") { ///////// newly integrated condition /////////
      setSessionType((SessionType==="countdown") ? "break" : "countdown");  
      setSessionTime(((SessionType==="countdown") ? SessionBreak : SessionLength));

    } else {
      setSessionOnPause(false);
      setSessionState("running");
    }
  }

Step 03

Store your session and break times as seconds, similar to your SessionTime state. Also, you don’t need three states for this; just breakLength and sessionLength would be sufficient. After setting the states, simply format them only for display…

  • I changed the values as integer-seconds as u suggested but however, I needed the SessionTime to be there to display the countdown. I wasn’t able to find a way to run & display a countdown with, just using my SessionBreak (breakLength) and SessionLength .
  const [SessionTime,setSessionTime] = React.useState(1500);
  const [SessionBreak,setSessionBreak] = React.useState(300);
  const [SessionLength,setSessionLength] = React.useState(1500);
<h4 id={`${props._type}-length`}>{props._val/60}</h4> /////// updated renderings //////////

Step 04

directly adjust the related time for example like this: setTime(timeLength + 60) or setTime(timeLength - 60) . For limits, you can use Math.max or Math.min :wink:. Additionally, you can pass states as props to child components. Consider this for your TimeSet component…

  • did exactly as u suggested. now my btn interactions wont exceeds maximums & wont go below negetives or zero (ranges in 1 to 60). for the TimeSet component, I’ve integrated a min-max before passing the actual value upon change. check the follwing onClick evts.
    <button
      id={`${props._type}-decrement`}
      onClick={() => props.setTimeFrame(Math.max((props._val/60)-1,1))}>
          <i className="fa-solid fa-circle-chevron-down"></i>
    </button>
    <h4 id={`${props._type}-length`}>{props._val/60}</h4>
    <button
      id={`${props._type}-increment`}
      onClick={() => props.setTimeFrame(Math.min((props._val/60)+1,60))}>
          <i className="fa-solid fa-circle-chevron-up"></i>
    </button>

### Extra Step

Given that all test cases are now passing and considering the small scope of this project, I have reintroduced the previously commented-out CSS-class toggle logic. This manipulation was confined to a single location and did not affect the test results. However, I plan to avoid such practices in the future. The current styling is now functioning as expected.

After making these adjustments, I believe you will pass all tests or identify the root of the problem…

  • even though all the test cases are passed, honestly I’m having a doubt which was the real logical problem I had in my code.
  1.       As I understood, around 80% it is the session time duration changes wont stick with the range on update. because at first I didn’t validate the value when passing it to the method, instead I validate it within the method right before updating the state. because of this sometimes when the update became faster, the values tends to jump over the specified range. I managed to handle this using Math.min & Math.max as u suggest.

  2.       up to 20% failure was cased by the next-stage time value not being ‘00:00’ & it’s not effectively updating when a jump occurs(from session to break or break to a session) because the setInterval cycle is kept running. I stopped this from happening using two if conditions,


        if(SessionTime-1===0) { ///////// newly integrated condition /////////
          document.getElementById("beep").play();
        }

        if(SessionTime===0) {
          clearInterval(timer);

          setTimeout(() => ManageSession("change"),1000);  ///////// newly integrated timeout + method call /////////
        } 

These are the identified issues that disrupted my test runs. Could you please confirm if these assumptions are correct? If they are, may I mark this reply as the solution to assist other newcomers who might encounter similar problems? If not, could you provide clarification on where I might be mistaken?

@0x74h51N & @lasjorg , I really appriciate ur dedication towards helping me to complete my final prj. so, THANKS AGAIN & Gud Luck! :smiley: :clinking_glasses:

Congratulations! That’s great to hear you pass all tests. You also explain very well how you manage your code and problems. This is more important than what you write in the code editor. Being able to explain what you’ve written shows that you are in control of your code, not the other way around :sweat_smile:. Trust me, in the beginning, many people write code without really understanding what it does.

However, there is some issues still on the table as you mentioned, lets focus them;

I mostly making story the problems to understand what they need or how can handle it. Lets make this one too;

There is a little countdown timer living in a small village… Okay, that’s a bit too much, let’s make it more realistic :sweat_smile:. There should be a countdown timer that continues running until it is paused or reset. However, this timer runs with session and break time conditions. If the session ends, the break should start, and after the break, the session should start again. So manage this interval should be clear if its reach zero or paused or reset.

However, you can’t clear an interval inside the interval.

 React.useEffect(() => {    
    const timer = setInterval(() => {
      if(SessionState==="running" && !SessionOnPause) {
        if(SessionTime-1===0) {
          document.getElementById("beep").play();
        }
        if(SessionTime===0) {
          clearInterval(timer); //not cleared
          setTimeout(() => ManageSession("change"),1000);
        } else {
          setTimeout(() => document.getElementById("time-left").classList.remove("activated"), 1000);
          setSessionTime(SessionTime-1);
        }
      }
    },1000);

    return () => clearInterval(timer);
  });

What if we clear the interval each time useEffect runs, at the beginning? We can add some dependencies to this useEffect so that it runs again when they get updated, like this [sessionType, sessionOnPause, sessionState]. When these states get updated, the useEffect will run again. But this time we can’t clear interval before declaration! What if you store to interval state with useState hook? :thinking:… Also you can clear interval inside of each function, for example manageSession or pause button click handler etc. This is not the only and certain solution but one way of the manage the interval.

And finally substraction of one seconds each interval, you can handle this with callback function inside to setTimer state updater. Here is the example:

const [timeLength, setTimer] = useState(500);
setTimer((prevSeconds) => {
      if (prevSeconds > 0) {
        return prevSeconds - 1;
      } else {
      //maybe this is the good place for play to beep
        return 0;
      }
    });

You’ve also learned function-based React very well and implemented it effectively. I checked the latest version of your app. For your future career, I want to give you some more tips:

In React, as I mentioned before, using Document class methods is not recommended. The reason is that you can manage everything with states. You’re already managing the time-left element classes the right way with following codes:

 <p id="time-left" 
       className={
               (ses_running) ? ((ses_on_countdown) ? "active activated" : "on_break") : "deactivated"}>
                    {minutes < 10 ? '0'+minutes : minutes}:{seconds < 10 ? '0'+seconds : seconds}
          </p>

So you don’t need to access this element by Document.getElementsById as following codes:

 const ManageSession = (type) => {

    let countdown = document.getElementById("time-left");
    
    if(SessionState==="stopped") {
      countdown.classList.add("activated");
    } else {

      if(SessionType!=="break") {

        if(!SessionOnPause) {
          countdown.classList.add("postponed");
        } else {
          (type==="reset") ? countdown.classList.add("stopped") : countdown.classList.add("activated");
        }
      }
    }

Also if you make specific changes for state condition, you can use style attribute. Example usage:

const Compy = () => {
  const [isActive, setIsActive] = useState(false);

  const divStyle = {
    backgroundColor: isActive ? 'lightblue' : 'lightcoral',
    color: isActive ? 'darkblue' : 'darkred',
    padding: '20px',
    borderRadius: '10px',
    textAlign: 'center',
  };

  return (
    <div>
      <div style={divStyle}>
      </div>
    </div>
  );
};

The other topic manage to element without re-render or using that element methods, for example play methods of audio element. useRef is very easy to use and makes it easier to manage DOM elements. Here is an example of using useRef for an audio element:

const AudioPlayer = () => {
  const audioRef = useRef(null);
  const [isPlaying, setIsPlaying] = useState(false);

  const handlePlayPause = () => {
    if (audioRef.current) {
      if (isPlaying) {
        audioRef.current.pause();
      } else {
        audioRef.current.play();
      }
      setIsPlaying(!isPlaying);
    }
  };

  return (
    <div>
      <audio ref={audioRef} src="path/to/your/audio/file.mp3" />
      <button onClick={handlePlayPause}>
        {isPlaying ? 'Pause' : 'Play'}
      </button>
    </div>
  );
};

And don’t forget, component names or Class names start with upper case letter, other things always start with lower case letter for example state names or function names…

1 Like

:grin: Thanks for,

  • pointing-out wt I hv missed during the completion
  • proper guidance within the explanations
  • being patient & keeping up with newbies like me during prjs

I have been intensely working over the past few days to complete this and I’m currently a bit exhausted. I :100: genuinely admire how ppl like u spend ur valuble time / ur dedication to the FCC community. I will update this project with the relevant changes & inform u shortly :handshake:t4:.

You’re welcome, it was a pleasure for me too. Understanding and solving problems in code written by others is both fun and educational. You’ll get to experience the joy of helping others and solving problems as you continue your journey, so don’t worry :grin:. Keep up the great work, and I’m looking forward to seeing your updated project. If you need any further assistance, feel free to reach out!

2 Likes