Home > OS >  why is it not adding second index element to the currrentText
why is it not adding second index element to the currrentText

Time:09-11

i am trying to perform typewriting effect through hooks. consider the text given as hello world . it is rendering only hlo world . it is not adding element at index 1 to the currentText. here is my code

import React,{useEffect,useState,useRef} from 'react';

function Typewriter({text}) {
    const index=useRef(0)
    const [currentText,setCurrentText]=useState('');
    useEffect(() => {
        const timeOutId=setTimeout( () =>{
            setCurrentText((value) => {
                return value text.charAt(index.current)
            });
            index.current  =1;
        },1000);
        return () =>{
            clearTimeout(timeOutId);
        }
        
    },[currentText,text])

  return (
    <p>{currentText}</p>
  )
}

CodePudding user response:

This is weird, but the solution is simple. The useEffect already depends on the currentText, so use it instead of the functional update option of set state:

const { useEffect,useState,useRef } = React;

function Typewriter({text}) {
  const index = useRef(0)
  const [currentText, setCurrentText] = useState('');

  useEffect(() => {
    const timeOutId = setTimeout(() => {
      setCurrentText(currentText   text.charAt(index.current));
      index.current  =1;
    }, 1000);

    return () => {
      clearTimeout(timeOutId);
    } 
  }, [currentText, text])

  return (
    <p>{currentText}</p>
  )
}

ReactDOM.createRoot(root)
  .render(
    <Typewriter text="hello world" />
  )
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

Not related, but a bit cleaner version of the type writer is to increment the index, and slice the required sub-string:

const { useEffect,useState,useRef } = React;

function Typewriter({text}) {
  const [index, setIndex] = useState(0);

  useEffect(() => {
    const timeOutId = setTimeout(() => {
      setIndex(index   1);
    }, 1000);

    return () => {
      clearTimeout(timeOutId);
    } 
  }, [index])

  return (
    <p>{text.substring(0, index)}</p>
  )
}

ReactDOM.createRoot(root)
  .render(
    <Typewriter text="hello world" />
  )
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<div id="root"></div>

CodePudding user response:

The reason could be this:

setCurrentText((value) => {
  return value text.charAt(index.current)
});
index.current  =1;

In StrictMode, useEffect is fired twice on mount. So, the index.current will be 2. This coupled together with the fact, that the callback you passed to setCurrentText might not be invoked immediately, it could be at the time it is invoked, it reads index.current which was increased on the next line.

  • Related