I am trying to make a typing effect, and its a very simple logic,,,but still i am not able to understand that why the 'e' in hello is always missing everything else is working fine. have not made the blinking cursor yet !!!!
CODE:
import { useState } from "react";
export function Type() {
let str = "hello my name is prateek"
const [char, setChar] = useState("");
function type() {
let i = 0;
let id = setInterval(() => {
setChar(prev => prev str[i]);
//console.log(i,"i")
// console.log(str[i])
i ;
if (i === str.length - 1) {
//console.log("hello")
clearInterval(id)
}
}, 1000);
}
return (<div>
<h1>{char}</h1>
<button onClick={type}>Type</button>
</div>)
}
OUTPUT
hllo my name is prateek
CodePudding user response:
I think you might have an asynchronous race condition:
- Async callbacks go into an event queue.
- The delay argument is like a minimum, see MDN for more on reasons delays can take longer
- Though I could use help finding the source for
dispatcher
(source code), I strongly suspect React Hooks likeuseState
use async callbacks and the event queue. - Assuming
useState
is an async callback, I suspect it has no delay.
One suggestion: use a loop and setTimeout()
with 1000*i
delay. The loop ensures you'll add each letter, the delay will add each letter ~1s apart.
Another suggestion: Dan Abramov did a really interesting blog post digging into this: Making setInterval declarative with React Hooks (2019). His solution explores a custom hook he writes, useInterval
(not part of React Hooks API), and explains what's happening with React render cycles and "sliding delay".
const { useState } = React;
function Type(){
let str = "hello my name is prateek"
const [char,setChar] = useState("");
function type(){
let i = 0;
const id = setInterval(()=>{
setChar(prev=>prev str[i]);
//console.log(i,"i")
// console.log(str[i])
i ;
if(i === str.length-1){
//console.log("hello")
clearInterval(id)
}
},1000);
}
return <div>
<h2>{char}</h2>
<button onClick={type}>Type</button>
</div>
}
function TypeWorking(){
let str = "hello my name is prateek"
const [char,setChar] = useState("");
function type(){
for(let i=0; i<str.length; i ) {
setTimeout(()=> setChar(prev=>prev str[i]), 1000*(i 1));
}
}
return <div>
<h2>{char}</h2>
<button onClick={type}>Type</button>
</div>
}
ReactDOM.createRoot(
document.getElementById('app-broken')
).render(<Type />)
ReactDOM.createRoot(
document.getElementById('app-working')
).render(<TypeWorking />)
<h1>Question (bug)</h1>
<div id="app-broken"></div>
<h1>Working</h1>
<div id="app-working"></div>
<script crossorigin src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.production.min.js"></script>
The question also mentions an animated cursor. One idea would be to use ::before
/::after
pseudo-elements (or another element like a <span>
) with an infinite CSS animation.
CodePudding user response:
I reworked Type function
and it has worked as what you want. Let's try it.
function type() {
const timerId = setInterval(() => {
setChar((prev) => {
if (prev.length >= str.length) {
clearInterval(timerId);
return prev;
}
return prev str[prev.length - 1 1];
});
}, 200);
}
You can refer at this link: https://codesandbox.io/s/musing-joji-rrpk6i?file=/src/App.js:155-451