i want to set a state in react in a loop going from 0 to “last step” but the state has to be set with a delay in other words:
-click a button, after the delay the state is set to 0, again after the delay the state is set to previous state +1, once the state is equal to “last step” end.
i tried this but is not working
JavaScript
x
8
1
const handleReplay = () => {
2
setTimeout(() => {
3
for (let i = 0; i < lastStep; i++) {
4
setStepNumber(i)
5
}
6
}, 500);
7
};
8
Advertisement
Answer
If the button starts a counter from 0
to lastStep
, then you need more than useState
:
Solution 1: states and effect
JavaScript
1
55
55
1
import { useEffect, useState } from "react";
2
3
// global constants:
4
const INIT_STEP = -1;
5
const LAST_STEP = 9; // for steps 0...9 (10 steps)
6
const DELAY = 1000;
7
8
export default () => {
9
// not active by default
10
const [active, setActive] = useState(false);
11
const [step, setStep] = useState(INIT_STEP);
12
13
useEffect(() => {
14
// if we are active, then start the incrementing timer
15
if (active) {
16
// set the internal step state
17
let currentStep = INIT_STEP;
18
19
// create an interval which will increment the step
20
const timer = setInterval(() => {
21
if (currentStep < LAST_STEP) {
22
currentStep = currentStep + 1;
23
setStep(currentStep);
24
} else {
25
// stop here because we have reached the end of steps
26
setActive(false);
27
}
28
}, DELAY);
29
30
// will be called when active is set to false
31
return () => clearInterval(timer);
32
}
33
}, [active]);
34
35
// external control
36
const handleButtonStart = () => {
37
setStep(INIT_STEP);
38
setActive(true);
39
};
40
const handleButtonStop = () => setActive(false);
41
42
return (
43
<div>
44
<h3>Solution 1: states and effect!</h3>
45
46
<div>
47
{active ? (step > INIT_STEP ? `Step ${step}` : "Pending") : "Stopped"}
48
</div>
49
50
<button onClick={handleButtonStart}>Start</button>
51
<button onClick={handleButtonStop}>Stop</button>
52
</div>
53
);
54
};
55
Solution 2: reducer
JavaScript
1
56
56
1
import { useEffect, useReducer } from "react";
2
3
const INIT_STEP = -1;
4
const LAST_STEP = 9; // for steps 0...9 (10 steps)
5
const DELAY = 1000;
6
7
const INIT_STATE = {
8
step: INIT_STEP,
9
active: false
10
};
11
12
const counterReducer = (state, action) => {
13
if (action.type === "start") {
14
return { step: INIT_STEP, active: true };
15
} else if (action.type === "stop") {
16
return { state, active: false };
17
} else if (action.type === "next" && state.active) {
18
if (state.step < LAST_STEP) {
19
return { state, step: state.step + 1 };
20
} else {
21
return { state, active: false };
22
}
23
} else {
24
return state; // no change
25
}
26
};
27
28
export default () => {
29
const [{ step, active }, dispatch] = useReducer(counterReducer, INIT_STATE);
30
31
useEffect(() => {
32
if (active) {
33
const timer = setInterval(() => {
34
dispatch({ type: "next" });
35
}, DELAY);
36
37
return () => clearInterval(timer);
38
}
39
}, [active]);
40
41
const handleButtonStart = () => dispatch({ type: "start" });
42
const handleButtonStop = () => dispatch({ type: "stop" });
43
44
return (
45
<div>
46
<h3>Solution 2: reducer!</h3>
47
<div>
48
{active ? (step > INIT_STEP ? `Step ${step}` : "Pending") : "Stopped"}
49
</div>
50
51
<button onClick={handleButtonStart}>Start</button>
52
<button onClick={handleButtonStop}>Stop</button>
53
</div>
54
);
55
};
56
The code can be tested here.