Push function is not working properly localStorage Javascript vanilla - javascript

I am doing a time-to-click game as you can imagine the fastest time would be the first place. I just want to have 3 scores. and have it on localstorage but every time I start a new game the actual score resets its value and it doesnt generate other scores. the 3 scores should have as value 0. I tried to push them as arrays but push function is not working well. at this point I am stuck. I dont know what to do. If u may help me, I would be really grateful, thanks!
let times = Array.from({
length: 3
})
let interval2;
// Timer CountUp
const timerCountUp = () => {
let times = 0;
let current = times;
interval2 = setInterval(() => {
times = current++
saveTimes(times)
return times
}, 1000);
}
// Saves the times to localStorage
const saveTimes = (times) => {
localStorage.setItem('times', JSON.stringify(times))
}
// Read existing notes from localStorage
const getSavedNotes = () => {
const timesJSON = localStorage.getItem('times')
try {
return timesJSON ? JSON.parse(timesJSON) : []
} catch (e) {
return []
}
}
//Button which starts the countUp
start.addEventListener('click', () => {
timerCountUp();
})
// Button which stops the countUp
document.querySelector('#start_button').addEventListener('click', (e) => {
console.log('click');
times.push(score = interval2)
getSavedTimes()
if (interval) {
clearInterval(interval);
clearInterval(interval2);
}
})

This:
// Button which stops the countUp
document.querySelector('#start_button').addEventListener('click', (e) => {
Probably should be this:
// Button which stops the countUp
document.querySelector('#stop_button').addEventListener('click', (e) => {
...considering you're already attaching an event-handler to start's 'click' event in the previous statement, and no-one would use #start_button as the id="" of a stop button.

Related

How do I update a state each second for next n seconds

I have a state variable resendTime and magicLinkState.
I would like to setResendTime to resendTime - 1 for next 10 seconds when magicLinkState becomes sent.
I have tried the following code but this does not update resendTime every second.
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
const timer = setInterval(() => setResendTime(resendTime - 1), 1000);
console.log(resendTime);
return () => clearInterval(timer);
}
}, []);
This code only updates the state for the first second. I have also tried passing dependency array but didn't worked.
How do I make it update the state so I achieve what is intended.
The general idea is correct in your code but needs some adjustments.
it's required to add the magicLinkState to the effect's dependencies otherwise it won't trigger when this gets the sent value but only if it's initially set with this value.
you should use the arrow syntax: setResendTime((prevResendTime) => prevResendTime - 1) in order to grab the correct state value each time
there is no need for a cleanup function in this effect since you want to clear the interval only after it's triggered and made 10 decreases to resendTime.
you should add some local count variable in order for the decrease to happen 10 times only and not forever
After these changes your code should look like below:
const decreaseResentTime = (prevResendTime) => prevResendTime - 1;
useEffect(() => {
let timer;
if (magicLinkState === "sent" && resendTime > 0) {
let count = 0;
timer = setInterval(() => {
if (count < 10) {
count++;
setResendTime(decreaseResentTime );
} else {
clearInterval(timer);
}
}, 1000);
}
}, [magicLinkState]);
You can find an example that demonstrates this solution in this CodeSandbox.
There are more improvements to be made here but they are based on your needs. For example if the magicLinkState changes to 'sent' and then to something else and then back to 'sent' within the 10 seconds, 2 intervals will run and decrease at a double rate.
You've just an issue of a stale closure over the initial resendTime state value. This is easily fixed by using a functional state update to correctly access the previous state value instead of whatever is closed over in the surround callback scope.
const timerRef = React.useRef();
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
const timer = setInterval(() => {
setResendTime(resendTime => resendTime - 1); // <-- functional update
}, 1000);
timerRef.current = timer;
return () => clearInterval(timer);
}
}, []);
Note also that because of the closure that if you want to log the resendTime state as it updates you'll need to use a another useEffect hook with a proper dependency. This is also where you'd move the logic to check if the interval is completed. Use a React ref to store a reference to the timer id.
useEffect(() => {
if (resendTime <= 0) {
clearInterval(timerRef.current);
}
console.log(resendTime);
}, [resendTime]);
Can I use timestamps instead of intervals? this function checks every second for 10 seconds when called once
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
let startTimeforTenSeconds = Date.now();
let startTimeforEachSecond = Date.now();
const nonBlockingCommand = new Promise((resolve, reject) => {
while (true) {
const currentTime = Date.now();
if (currentTime - startTimeforEachSecond >= 1000) {
startTimeforEachSecond = Date.now();
console.log("1 seconds have passed. set resend time here");
// code for resending time here
setResendTime(resendTime - 1);
console.log(resendTime );
}
if (currentTime - startTimeforTenSeconds >= 10000) {
console.log("10 seconds have passed. stop checking.");
resolve("10 seconds have passed.");
break;
}
}
});
nonBlockingCommand.then(() => console.log('function is done'));
}
}, []);
EDIT: Result screenshot:

The parameter I'm passing through a async function is undefined in the function

I have an async function that on page load it runs a function that gets a JSON file and stores it in songResults. it then gets a random object from the JSON and takes some parameters from it and uses them to set the sources for some elements in my HTML file. I am having an issue with the callback for the event listener, the parameter I am passing through guessingGame() is undefined inside the guessingGame() function and I'm not sure why. any help would be muchly appreciated.
JS code
//A async function to run when the page loads to get the first song and album cover to display.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input === songPath) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
What a json file response looks like
{
"song_name": "Listen to Us",
"release_date": "1/05/2012",
"album": "Home Brew",
"photo_path": "/pictures/home-brew.jpg",
"song_path": "/songs/homebrewski-listenToUs.mp3"
}
so the new code updates it kind of it works the first time you guess but the second time you guess it says your incorrect then correct and keeps looping heaps really weird and lags out the browser.
const onPageLoad = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
//An event listener for the submit button to run the guessing game function.
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
//async function for when the button is clicked to check the answer in the input box to the json data.
const guessingGame = async (songPath) => {
//get the value of the input box
let input = document.getElementById("guessInputBox").value;
//check if the value of the input box matches the song path in the json data
if (input) {
if (input.toLowerCase() === songPath.toLowerCase()) {
alert('correct')
score++;
alert("that took " + score + " attempts");
score = 0;
changeSong();
} else {
alert('incorrect')
alert(songPath);
score++;
};
};
};
//need to change this to an async function once figured out how to store data.
const changeSong = async () => {
let songResults = await getSongData();
let randomSong = songResults[Math.floor(Math.random() * songResults.length)];
audioSound.src = randomSong.song_path;
audioSound.load();
albumCover.src = randomSong.photo_path;
submitButton.addEventListener("click", () => {
guessingGame(randomSong.song_name);
});
};
Updated Answer
Thank you for posting more code. It looks like you may have mistakenly passed along the song_name instead of the song_path:
// From the onPageLoad and changeSong functions
guessingGame(randomSong.song_name);
// From the guessingGame function
input.toLowerCase() === songPath.toLowerCase()
Another thing to consider is your click-handler; you're adding one to the submitButton every time a song is loaded. As such, the first time you click the button, a single event handler is called. The second time you click, two are called. The third time, three, etc.
Your submit button behavior really only needs to be set once, and forgotten. It's job is to do one thing: see if the user's selection matches the currently-playing song. And, if the user got the correct answer, load another song:
// Minimal game state
let score = 0;
let songs = [];
let songIndex = -1;
// References to game elements
const submit = document.querySelector("#submit")
const selection = document.querySelector("options");
// Begin game functionality
async function onPageLoad () {
// Load meta data for all songs into `songs`
songs = await getSongData();
// TODO: Populate #options based on `songs` data
setRandomSong();
}
function setRandomSong () {
songIndex = Math.floor( Math.random() * songs.length );
audioSound.src = songs[ songIndex ].path;
albumCover.src = songs[ songIndex ].photo;
}
// This will be called whenever a song is changed
audioSound.addEventListener( "load", function audioLoaded () {
console.log( "Audio has loaded, game can be played." );
});
// We need only a single handler for our guess-button
submit.addEventListener( "click", function guess () {
// Get our values to compare
const guess = selection.value;
const answer = songs[ songIndex ].name;
// Increment the number of attempts
score += 1;
// Check lowercase values for equality
// If the answer is false, share number of attempts
if ( guess.toLowerCase() !== answer.toLowerCase() ) {
alert( `You have guessed ${ score } times.` );
return;
}
// If the answer was correct, reset game state
alert( `Correct! And after ${ score } guesses.` );
score = 0;
setRandomSong();
});
Note, this code is untested, but should clearly define a suitable approach to your problem. Please feel free to engage further within the comments as needed.
Original Answer
The addEventListener method expects a function as the second argument. Look closely at your code:
submitButton.addEventListener("click", guessingGame(randomSong.song_path))
Note that you're executing guessingGame, rather than referencing it. Instead, you'd want to provide another function:
submitButton.addEventListener("click", function () {
guessingGame( randomSong.song_path );
});
Now, when the submitButton is clicked, our anonymous function will be called, which in turn will pass randomSong.song_path to guessingGame.

How can I wait for a click to be executed?

I am trying to figure out how I can update the playerValue for my Rock Scissor Paper game. I have a function that registers a button click and should then update the playerValue accordingly(1,2 or three depending on which button was clicked). The problem is, when I call the function, the playerValue remains 0 and I don't know what I need to change to fix that. playerValue itself is defined at the very beginning of my file. It starts out as 0.
Here is my JavaScript code(or at least the relevant part of it):
//register button click and:
function player_choose_value(){
//check which button has been clicked -> rock 1, scissor 2 or paper 3
btnRock.addEventListener("click", () =>{
playerValue = 1;
});
btnScissor.addEventListener("click", () =>{
playerValue = 2;
});
btnPaper.addEventListener("click", () =>{
playerValue = 3;
});
}
This here is where the playerValue is meant to be used. The playerValue is always 0. I think that is because the player_choose_value() function does not wait for the click event to happen. So the function is executed but the user does not have the chance to actually click a button, so it stays 0:
function play_round(){
let computerValue = computer_choose_value();
player_choose_value();//is always zero
console.log(playerValue);
won_tie_lost(computerValue, playerValue);
}
I was wondering how I could add a "wait for one of the three buttons to be clicked" functionality?
In your case, player_choose_value doesn't wait until the player has actually picked a value.
You could probably do this using async await/promises:
function player_choose_value(){
return new Promise(resolve => {
bntRock.onclick = () => resolve(1)
btnScissor.onclick = () => resolve(2)
btnPaper.onclick = () => resolve(3)
})
}
async function play_round(){
let computerValue = computer_choose_value();
const playerValue = await player_choose_value();
console.log(playerValue);
won_tie_lost(computerValue, playerValue);
}
;(async function main() {
while (true) { // Keep repeating the game
await play_round()
}
})()
Use promise.
function player_choose_value() {
return new Promise((resolve, reject) => {
//check which button has been clicked -> rock 1, scissor 2 or paper 3
document.querySelector('#btn1').addEventListener("click", () => {
resolve(1);
});
document.querySelector('#btn2').addEventListener("click", () => {
resolve(2);
});
document.querySelector('#btn3').addEventListener("click", () => {
resolve(3);
});
});
}
player_choose_value().then(
play_value => {
alert(play_value);
// won_tie_lost...
// other code 2...
}
);
// other code 1...
<button id='btn1'>btn1</button>
<button id='btn2'>btn2</button>
<button id='btn3'>btn3</button>
player_choose_value will called first. It returns a promise that is pending.
The engine will continue to execute other code 1 and the code in then(play_value => { block will not be executed until one of the resolves is called (the promise is fulfilled).
Here is another pattern. I think it suits your needs better.
document.querySelectorAll('button').forEach((button, index) => {
button.addEventListener('click', () => {
let playerValue = index;
let computerValue = computer_choose_value();
play_round(playerValue, computerValue);
})
});
function play_round(playerValue, computerValue) {
// Disable the buttons if needed
alert(`${playerValue} ${computerValue}`);
// won_tie_lost(computerValue, playerValue);
}
function computer_choose_value() {
return ~~(Math.random() * 3);
}
// start a round by enable the buttons
<button id='btn1'>btn1</button>
<button id='btn2'>btn2</button>
<button id='btn3'>btn3</button>
You're going at it in a totally wrong way.
You should have play_round() be called by the button clicks, otherwise you will add event listeners on the button every single round.
btnRock.addEventListener("click", play_round);
btnScissor.addEventListener("click", play_round);
btnPaper.addEventListener("click", play_round);
function play_round(event){
let playerValue = event.target.dataset.value;
let computerValue = computer_choose_value();
won_tie_lost(computerValue, playerValue);
}
function computer_choose_value() {
return Math.floor(Math.random() * 3) + 1;
}
function won_tie_lost(playerValue, computerValue) {
console.log(`player: ${playerValue}, computer: ${computerValue}`);
}
<button id="btnRock" data-value="1">Rock</button>
<button id="btnScissor" data-value="2">Scissor</button>
<button id="btnPaper" data-value="3">Paper</button>

How set interval() and clear interval() works under the hood

this is what I have done so far. The function for click on other buttons is to have an array filtered from data array and provide back and forth buttons functionality. the specific part about which i want to know is this piece of code. and could it be done more efficiently? i am sure its wrong but can't point why clearnInterval() doesn't work and i get an error after 4 seconds even though the array is done iterating.
mainBtns.forEach(mainBtn => {
mainBtn.addEventListener('click', () => {
mainBtn.style.textDecoration = 'underline';
//homeBtn
const setInt = null;
if(mainBtn.dataset.name === 'home'){
setInt = setInterval(() => {
i++;
targetTest.src = data[i].src;
targetTest.alt = data[i].alt;
console.log(targetTest);
functionalBtns(data);
if(i > length){
clearTimeout();
targetTest.src = data[0].src;
targetTest.alt = data[0].alt;
}
},4000);
}

React setTimeout with Loop

I am pulling documents from Firebase, running calculations on them and separating the results into an array. I have an event listener in place to update the array with new data as it is populated.
I am using setTimeout to loop through an array which works perfectly with the initial data load, but occasionally, when the array is updated with new information, the setTimeout glitches and either begins looping through from the beginning rather than continuing the loop, or creates a visual issue where the loop doubles.
Everything lives inside of a useEffect to ensure that data changes are only mapped when the listener finds new data. I am wondering if I need to find a way to get the setTimeout outside of this effect? Is there something I'm missing to avoid this issue?
const TeamDetails = (props) => {
const [teamState, setTeamState] = useState(props.pushData)
const [slide, setSlide] = useState(0)
useEffect(() => {
setTeamState(props.pushData)
}, [props.pushData])
useEffect(()=> {
const teams = teamState.filter(x => x.regData.onTeam !== "null" && x.regData.onTeam !== undefined)
const listTeams = [...new Set(teams.map((x) => x.regData.onTeam).sort())];
const allTeamData = () => {
let array = []
listTeams.forEach((doc) => {
//ALL CALCULATIONS HAPPEN HERE
}
array.push(state)
})
return array
}
function SetData() {
var data = allTeamData()[slide];
//THIS FUNCTION BREAKS DOWN THE ARRAY INTO INDIVIDUAL HTML ELEMENTS
}
SetData()
setTimeout(() => {
if (slide === (allTeamData().length - 1)) {
setSlide(0);
}
if (slide !== (allTeamData().length - 1)) {
setSlide(slide + 1);
}
SetData();
console.log(slide)
}, 8000)
}, [teamState, slide]);

Categories