How to use setInterval and setTimeout with React hooks - reactjs

I want to loop through an array of strings
When a new string, from the array, is selected I want to print out a
substring of the selected string every 0.1 second
After the entire string is printed I want to pause and then select the
next string in the array
Repeat
eg ['one', 'two']
output:
o
on
one
// pause 1 second
t
tw
two
// pause 1 second
o
on
one
// pause 1 second
I have tried this but it only loops through once
useEffect(() => {
let i = 0
function increment() {
i++
console.log(i)
}
const incrementTimer = setInterval(increment, 100)
setInterval(() => {
clearInterval(incrementTimer)
}, 1000)
}, [])

I created a codesandbox link: https://codesandbox.io/s/recursing-bash-hkmqrc?file=/src/App.js
This is done by rxjs as you are dealing with time related actions. rxjs is the best tool. After you reload the page, it will start to log values as you expect in 1s.
Can you please let me know if the result is what you want? Thanks.

Related

Can't add anything correctly to useState array?

I'm new in React. I writed that code by following a tutorial.
I tried many ways but couldn't add new arrays correctly into useState array.
function TodoApp(){
const [liste, setListe] = useState( [] );
function listeyeEkle(e) {
console.log(e); // {yazi: "First", id: 0} It's great here, no problems.
setListe( [e, ...liste] ) // Console log in Picture 1
setListe( e ); // Console log in Picture 2
console.log(liste);
}
Picture 1
Picture 2
Something going wrong and it's adding a empty object into array??
Do you have any idea?
Thanks!
The way you wrote it, you are setting the correct array first:
setListe( [e, ...liste] )
Afterwards you are setting the state to the value of the inputs element:
setListe( e )
I think you should remove this line? Then it will work! But might not show up in console.log(liste), due to the async nature of setState()!

'setState' of useState hook occurs twice by one call

There is a board with squares their value relies on an array, it is handled with useState hook. Every click should raise the value by one, but unfortunately, it raises it by two (except the first click).
My questions are:
(1) Why is it happen, (2) how to avoid it, and, in general, (3) is there a better way to handle such an array with hooks.
let emptyBoard = Array.from({ length: parseInt(props.rows, 10) }, () =>
new Array(parseInt(props.columns, 10)).fill(0)
);
const [squaresValues, setSquaresValue] = useState(emptyBoard);
function onClick(id) {
const [rowIndex, cellIndex] = id;
console.log("the " + id + " square was clicked");
setSquaresValue(prevValues => {
let newBoard = [...prevValues];
console.log("before: " + newBoard[rowIndex][cellIndex]);
newBoard[rowIndex][cellIndex] = newBoard[rowIndex][cellIndex] + 1;
console.log("after: " + newBoard[rowIndex][cellIndex]);
return newBoard;
}
);
}
The log:
the 3,0 square was clicked
before: 0
after: 1
the 3,0 square was clicked
before: 1
after: 2
before: 2
after: 3
As can be seen, from the second click the value is raised twice by every click.
You were still mutating state, if you have pure components then they won't re render when mutating. Doing the full state copy with JSON.parse is a bad idea if you have pure components because everything will be re rendered.
let newBoard = [...prevValues];
newBoard[rowIndex] = [...newBoard[rowIndex]];
newBoard[rowIndex][cellIndex] =
newBoard[rowIndex][cellIndex] + 1;
As mentioned by Udaya Prakash in the comment above, it's being called twice to make sure your setState is independent and idempotent. So, if I understand correctly, it being called twice is not a bug, but your values being changed the second time is.
Here's Dan Abramov's comment from the same GitHub issue:
It is expected that setState updaters will run twice in strict mode in development. This helps ensure the code doesn't rely on them running a single time (which wouldn't be the case if an async render was aborted and alter restarted). If your setState updaters are pure functions (as they should be) then this shouldn't affect the logic of your application.
We can fix it by deep-copying your prevValues instead of shallow-copying with spread operator. As you might already know, there are multiple ways to deep copy your object, we could go with JSON.parse(JSON.stringify(...) for now, which you can replace with your favorite kind from here
setSquaresValue(prevValues => {
let newBoard = JSON.parse(JSON.stringify(prevValues)); // <<< this
console.log("before: " + newBoard[rowIndex][cellIndex]);
newBoard[rowIndex][cellIndex] = newBoard[rowIndex][cellIndex] + 1;
console.log("after: " + newBoard[rowIndex][cellIndex]);
return newBoard;
});
I've kind of simulated it in codesandbox here if you want to play around.

Get readline input as an array

I'm doing a programming challenge right now but I'm struggling with getting the input right. There is no feedback on my output, only "error" which makes it really hard to debug for me. Here is the input:
4 2
1 4
2 9
4 7
5 8
and I want to collect it like this:
[4, 2, 1, 4, 2, 9, 4, 7, 5, 8];
The test environment tells me to work with the input like this:
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('line', (line) => {
var nums = line.split(' ');
/*Solve the test case and output the answer*/
});
How ever I must be getting the wrong array for my nums variable.I tried a bunch of approaches (splitting by /n and whitespaces, iterating with for loop and push... working with rl.close...) but as there is virtually no feedback on my input I am getting kind of desperate here. A simple interface which tells me my program output would help...
SOLUTION
var nums = [];
rl.on("line", line => {
let newLine = line.split(" ");
newLine.map(line => nums.push(line));
});
rl.on("close", function() {
console.log(nums)
});
It was possible for me to debug via the terminal once I got the input right.
The 'line' event would be emitted every time when rl read a new line, so you should just declare your nums outside of the callback function of the listener. Something like this:
...
var sums = [];
rl.on('line', (line) => {
let newNumbers = line.split(' '); // [4, 2]
sums.concat(newNumbers);
});
...
You may want to understand how event emitter and event listener work in JavaScript.
I think there is nothing for you even you register an 'error' event listener because everything is working as expected. And I believe there must be an event like 'end' or 'closed' which would be emitted after rl read all content of the input, and you can console.log your sums array there, I believe you can get your expected result.
You shouldn't handle the sums array right after the closing brackets, JavaScript is asynchronous, so those codes would be executed before all of the lines are read. If there is a method like rl.close you should call it in the situation like:
rl.on('line', (line) => {
...
if (line === undefined) { // or any terminal character which createInterface would return.
rl.close();
}
});
And I believe rl.close() will emit the event like what I said above , something like 'end' or 'closed', put the code to handle the final sums there.
A simple interface which tells me my program output would help...
You can run your nodejs application with nodejs! You can download and install it, and then in your terminal node yourjscodefile.js. When you console.log(variable); it will output in the terminal.

OpenLayers loop layers function

In OpenLayers, I have a button (show), when I press on it, I got a layer on the map
My question: How can I show many layers consequently With a certain frame rate
(Something like a loop!)
to show a new/other layer every X seconds, use setInterval
let's assume that you somehow got an array of dates for which you have tiles, and a function displayLayerByDate(dateObject) that can show the layer.
function animatedLayers(arrayOfDates) {
let currentIdx = 0;
const handle = setInterval(function() {
const currentDate = arrayOfDates[currentIdx];
displayLayerByDate(dateObject);
}, 5000);
}

Converting HH:MM String into Time Object

In my Ionic AngularJS Project, I have a string called startingTime set as 08:30
I will have a ng-repeat loop which will then make a series of items with an increasing time.
For example , the first item in the list will have 08:30, second item in the list 09:00 etc ( the increment will depend on another variable so its not 30mins every loop )
The problem is I cant seem to turn the string into a Time Object and i get null displayed most of the time if i try the following
$scope.newStartTime = moment(startTime).format('HH-MM');
OR
$scope.newStartTime = var date = new Date(startTime);
So my 2 main questions are.
How do i convert a string "08:30" into a time object ?
After its a time object, how can i make a loop so the time increases by minutes after every loop ?
Thank you.
This method work for me, that transform your startingTime into a MomentObject :
var startingTime = "8:30";
var date = moment(startingTime, "HHmm");`
console.log(date);
plunker demo
For create your timer you can make something like that :
$scope.timer = moment(startingTime,"HHmm");
$scope.setInterval = setInterval(function () { $scope.myTimer(); }, 1000);
$scope.myTimer = function () {
$scope.timer = $scope.timer.add('s', 1);
};

Resources