I am making a simple space shooter style game. The bad guys will be sent to the screen in intervals (similar to tetris).
The bad guys have all their touch functionality working when I pass them in an array manually but when I try to send them to the screen in intervals they lose all touch functionality.
I first tried a useEffect hook with setInterval. The interval would filter the array of bad guys. That was where the problem first popped up. Then, I tried a custom useInterval hook and got the same result.
Lastly - rather than set the state of the array of onscreen villains, I thought I would keep the villains out of the setInterval and just increment indexes, and return a slice of them. This did allow the first villain to gain touch ability but ... I was again frustrated by failure.
Here is the offending code -
aliensArray is the array of alien components for the user to battle.
function runTheInterval(){
const difference = slice.right - slice.left;
if(slice.right < aliensArray.length-1){
slice.right += 1;
}
if (slice.left < slice.right && difference > 3 && difference < 5){
slice.left += 1;
}else if (slice.left < slice.right && slice.right === aliensArray.length-1){
slice.left += 1;
}
setSlice(() => {
return { right: slice.right, left: slice.left }
})
}
const interValue = useInterval(runTheInterval, 4000);
----Other code----
const liveOnScreen = aliensArray.slice(slice.left, slice.right);
<>
{liveOnScreen}</>
So the only thing to pass through the interval is the indices of the villains array to be filtered. What am I doing wrong??
Thanks
Here is the custom useInterval hook that I used.
const intervalRef = useRef(null);
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
const tick = () => savedCallback.current();
if (typeof delay === 'number') {
intervalRef.current = window.setInterval(tick, delay);
return () => window.clearInterval(intervalRef.current);
}
}, [delay]);
return intervalRef;
}```
Related
I want to check on load if the object window has the property VL.refCode.
I use viral loops for my website and viral loop put in window.VL object the property refCode when a user is registered.
I put a for loop in a useEffect to check if this property exists or not, but unfortunately it becomes an infinite loop.
useEffect(() => {
if(window.VL){
if(window.VL.refCode){
setCryptoButtonMessage(t('common:cryptoButton_already_register'))
console.log('useEffect :user already register')
}
else{
console.log('useEffect : window.vl.refcode not exist')
}
}
else {
setCryptoButtonMessage(t('common:cryptoButton_title_waitlist'))
}
// t is for translation with i18n
},[t]);
This solution doesn't work because viral loop create window.VL object 1 to 2sec max after the first render.
If I put a setTimeout it's not a valid solution for users with mobile device / slow 3g / without fiber
So i use this solution
useEffect(() => {
let animation;
const check = () => {
console.log('je suis dans check');
console.log('je suis dans for');
if ((Object.prototype.hasOwnProperty.call(window.VL, 'refCode'))) {
console.log('je trouve refCode ');
setCryptoButtonMessage(t('common:cryptoButton_already_register'))
cancelAnimationFrame(animation);
return;
}
animation = requestAnimationFrame(check);
};
animation = requestAnimationFrame(check);
},[t]);
But this solution will never stop until window.VL.refCode exist ... not really best solution for website performance ...
I try to put a " simulate timer " but it becomes an infinite loop ...
useEffect(() => {
for (let i = 1; i < 10000; i += 1) {
let animation;
const check = () => {
if ((Object.prototype.hasOwnProperty.call(window.VL, 'refCode'))) {
setCryptoButtonMessage(t('common:cryptoButton_already_register'))
cancelAnimationFrame(animation);
return;
}
animation = requestAnimationFrame(check);
};
animation = requestAnimationFrame(check);
}
},[t]);
I don't know of any way of being automatically told when an object gets created on the window - so as far as I know the only way to do this is with a poll. If it was me, I'd abstract the logic for polling for the ref code into a new hook:
import { useEffect, useRef, useState } from 'react';
const useRefCode = (pollTimeMillis = 1000) => {
const timeoutRef = useRef(undefined);
const [refCode, setRefCode] = useState(window.VL?.refCode);
const cancelTimeout = () => {
if (!timeoutRef.current) return;
clearTimeout(timeoutRef.current);
timeoutRef.current = undefined;
};
useEffect(() => {
if (refCode) return;
setTimeout(() =>
setRefCode(window.VL?.refCode || undefined),
pollTimeMillis
);
return cancelTimeout;
}, [refCode]);
return refCode;
}
Then your other component that wants to change the message based on the ref code becomes very simple:
const OtherComponent = () => {
const refCode = useRefCode();
const translationKey = refCode
? 'common:cryptoButton_already_register'
: 'common:cryptoButton_title_waitlist';
return (
<div>{t(translationKey)}</div>
);
}
I use setInterval in my code to make my slider work. It should change image each 5 second . But I see that when I use it , it consume new memory all the time . How can I use setInterval without consuming new memory ?
const Slider = ({ children, sliderIndex, setSlideIndex }) => {
const classes = useStyles()
useInterval(() => {
setSlideIndex((sliderIndex + 1) % children.length)
}, 5000)
const animateFunc = useCallback(
(child, indx) => {
return (
(indx === sliderIndex && (
<div className={classes.root} key={indx}>
{children[sliderIndex].image}
</div>
)) ||
null
)
},
[children, classes.root, sliderIndex]
)
return children.map(animateFunc)
}
i use #use-it/interval hook for it https://www.npmjs.com/package/#use-it/interval
It seems unlikely to me that the interval is actually causing your issue here.
Below is a snippit that will run for two minutes - printing a new line to the console each second. Take a look at the memory usage over time in this page - it won't grow. You can even add a heavy for/while function into it if you like. I think your issue is elsewhere in your code and there are some good comments above to follow up.
let n = 1;
let myInterval = setInterval(function(){ console.log("hello number:" + n++); shouldIStop(120) }, 1000);
function shouldIStop(maxCount){
if (n >= maxCount){
clearInterval(myInterval);
}
}
I am doing a time-to-click score table but first I have to localstorage each time I get in a game but I dont know how I have been trying everything but it still not working, I need to finish this fast, and I need help... otherwise I would try everyday to resolve this alone because I know that's the way to learn.. When I press the finished button It says that times.push() is not a function.
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 = getSavedNotes()
times.push({
score: interval2
})
if (interval) {
clearInterval(interval);
clearInterval(interval2);
}
})
You probably need to change the line times = JSON.parse(localStorage.times || []) to times = JSON.parse(localStorage.times) || [] this makes sure that if the parsing fails it will still be an array.
Even better is wrap them with try-catch just in case if your JSON string is corrupted and check if the time is an array in the finally if not set it to empty array.
I have created a localStorage that adds +1 every time the user visits my app. The problem is that every time I refresh the page, the value goes back to 0.
This is my code:
localStorage.setItem('timesVisited', 0);
useEffect(() => {
if (localStorage.getItem('timesVisited') < 5) {
let timesVisited = parseInt(localStorage.getItem('timesVisited'));
localStorage.setItem('timesVisited', ++timesVisited);
}
},[])
The idea is to make the localStorage sum +1 until it reaches 5 but it keeps on 0. What I'm doing wrong?
This line will always run and resets any previous value
localStorage.setItem('timesVisited', 0);
turn it into
if(!localStorage.getItem('timesVisited')){
localStorage.setItem('timesVisited', 0)
}
Try something like this, if localstorage not set then set 0 otherwise increment
useEffect(() => {
if (!localStorage.getItem('timesVisited')) {
localStorage.setItem('timesVisited', 0);
} else if (localStorage.getItem('timesVisited') < 5) {
let timesVisited = parseInt(localStorage.getItem('timesVisited'));
localStorage.setItem('timesVisited', ++timesVisited);
}
}, [])
If the lines you provided in the component's body, then not only will it be called every time a user visits your app, but every time the component renders.
For the behaviour you're looking for, you should load the value, and set it to 0 if it doesn't exist. You should do this outside of your component, too.
Also, note that localStorage only stores strings, so you'll want to parse what you fetch from it.
const storedValue = localStorage.getItem('timesVisited')
// localStorage only stores string
const visitsCount = storedValue ? parseInt(storedValue) : 0
function myComponent = () => {
useEffect(() => {
if(visitsCount >= 5){
return
}
localStorage.setItem('timesVisited', ++visitsCount);
}, [])
}
Every minute I have a script that push a new record in my firebase database.
What i want is delete the last records when length of the list reach a fixed value.
I have been through the doc and other post and the thing I have found so far is something like that :
// Max number of lines of the chat history.
const MAX_ARDUINO = 10;
exports.arduinoResponseLength = functions.database.ref('/arduinoResponse/{res}').onWrite(event => {
const parentRef = event.data.ref.parent;
return parentRef.once('value').then(snapshot => {
if (snapshot.numChildren() >= MAX_ARDUINO) {
let childCount = 0;
let updates = {};
snapshot.forEach(function(child) {
if (++childCount <= snapshot.numChildren() - MAX_ARDUINO) {
updates[child.key] = null;
}
});
// Update the parent. This effectively removes the extra children.
return parentRef.update(updates);
}
});
});
The problem is : onWrite seems to download all the related data every time it is triggered.
This is a pretty good process when the list is not so long. But I have like 4000 records, and every month it seems that I screw up my firebase download quota with that.
Does anyone would know how to handle this kind of situation ?
Ok so at the end I came with 3 functions. One update the number of arduino records, one totally recount it if the counter is missing. The last one use the counter to make a query using the limitToFirst filter so it retrieve only the relevant data to remove.
It is actually a combination of those two example provided by Firebase :
https://github.com/firebase/functions-samples/tree/master/limit-children
https://github.com/firebase/functions-samples/tree/master/child-count
Here is my final result
const MAX_ARDUINO = 1500;
exports.deleteOldArduino = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
return countRef.once('value').then(snapCount => {
return collectionRef.limitToFirst(snapCount.val() - MAX_ARDUINO).transaction(snapshot => {
snapshot = null;
return snapshot;
})
});
});
exports.trackArduinoLength = functions.database.ref('/arduinoResponse/{resId}/timestamp').onWrite(event => {
const collectionRef = event.data.ref.parent.parent;
const countRef = collectionRef.parent.child('arduinoResCount');
// Return the promise from countRef.transaction() so our function
// waits for this async event to complete before it exits.
return countRef.transaction(current => {
if (event.data.exists() && !event.data.previous.exists()) {
return (current || 0) + 1;
} else if (!event.data.exists() && event.data.previous.exists()) {
return (current || 0) - 1;
}
}).then(() => {
console.log('Counter updated.');
});
});
exports.recountArduino = functions.database.ref('/arduinoResCount').onWrite(event => {
if (!event.data.exists()) {
const counterRef = event.data.ref;
const collectionRef = counterRef.parent.child('arduinoResponse');
// Return the promise from counterRef.set() so our function
// waits for this async event to complete before it exits.
return collectionRef.once('value')
.then(arduinoRes => counterRef.set(arduinoRes.numChildren()));
}
});
I have not tested it yet but soon I will post my result !
I also heard that one day Firebase will add a "size" query, that is definitely missing in my opinion.