Home > OS >  "e" missing in "hello", i dont know why? React useState problem
"e" missing in "hello", i dont know why? React useState problem

Time:11-08

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 like useState 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

  • Related