简体   繁体   中英

why is it not adding second index element to the currrentText

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>
  )
}

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>

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.

You may consider an alternate solution as proposed in another answer if that suits your needs.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM