React Array State Update Not Consistant
Using setInterval
and an index
to keep track of position. Check out example here https://codesandbox.io/s/bold-wind-sq8lb?file=/src/App.js:
import { useEffect, useState } from "react";
export default function App() {
const sentence =
"The story, characters, and events, in this game are entirely fictional. Any similarities to actual people, places and events are entirely coicidental.";
const [dialogue, setDialogue] = useState("");
const [index, setIndex] = useState(0);
const letterArray = sentence.split(/\b/);
const handleChangeValue = () => {
if (letterArray[index] !== undefined) {
setDialogue((dialogue) => dialogue + letterArray[index]);
}
};
useEffect(() => {
// update the dialogue here
const interval = setInterval(() => {
if (index < letterArray.length) {
setIndex((index) => index + 1);
}
}, 100);
return () => clearInterval(interval);
});
useEffect(() => {
// update the dialogue here
handleChangeValue();
console.log(`New dialogue: ${dialogue}`);
}, [index]);
return (
<div className="App">
<p>{dialogue}</p>
</div>
);
}
Don't use setTimeout directly within the React component return, it will setup massive of timers into Javascript runtime and never be recycled.
If the setTimeout or even setInterval is what you needed, wrap them with useEffect would be the idea
By guessing what you're trying to do, this is an example code. It will display a word every 0.5s to the screen till the end of params
export default function DialogueBox(params){
const params = "The story, characters, and events, in this game are entirely fictional. Any similarities to actual people, places and events are entirely coicidental.";
const letters = params.split(/\b/)
const [dialogue, setDialogue] = useState([]);
useEffect(() => {
const timers = []
letters.forEach((letter, index) => {
timers.push(
setTimeout(() => {
console.log(`Display at 500*${index} ms: ${letter}`)
setDialogue(originDialogue => [...originDialogue, letter])
}, 500*index)
)
})
return () => timers.map((timer) => clearTimeout(timer)) // clean up timers to prevent memory leak
}, [params]) // params here as dependency of useEffect, see offical document for useEffect for detail
return (
<div className="dialogueBox">
{dialogue.map((letter) => (
<p>{letter}</p>
))}
</div>
)
}