代码之家  ›  专栏  ›  技术社区  ›  Adam Basha

为什么for循环对同一索引计数两次?

  •  0
  • Adam Basha  · 技术社区  · 2 年前

    我对React和timout函数有一个奇怪的问题。 出于某种原因,我希望将一个字符串添加到 useEffect 一封接一封。所以我写了这个代码:

    const [placeholder, setPlaceholder] = useState < string > ('')
    
    useEffect(() => {
        const str = "How may I help you today?";
        const letters = Array.from(str);
    
        const addLetterWithDelay = (index: number) => {
            if (index < letters.length) {
                setTimeout(() => {
                    setPlaceholder(prevPlaceholder => prevPlaceholder + letters[index]);
                    addLetterWithDelay(index + 1);
                }, 1000); // Delay of 1 second
            }
        };
    
        addLetterWithDelay(0); // Start adding letters from index 0
    }, []);
    

    问题是这封信设置了两次 所以输出将是:HHooww mmaayy II hheellpp yoouu ttooddaayy?? 即使我用一个语句检查占位符的最后一个字符是否与字母[index]相同,它仍然会打印出它不相同并继续执行

    2 回复  |  直到 2 年前
        1
  •  1
  •   deviep11    2 年前

    将useEffect修改为:

     useEffect(() => {
        const str = "How may I help you today?";
        const letters = Array.from(str);
        let timerId: NodeJS.Timeout; // Declare a variable to hold the timeout ID
    
        const addLetterWithDelay = (index: number) => {
            if (index < letters.length) {
                timerId = setTimeout(() => {
                    setPlaceholder(prevPlaceholder => prevPlaceholder + letters[index]);
                    addLetterWithDelay(index + 1);
                }, 1000); // Delay of 1 second
            }
        };
    
        addLetterWithDelay(0); // Start adding letters from index 0
    
        return () => clearTimeout(timerId); // Clear the timeout when the component is unmounted or the useEffect hook re-runs
    }, []);
    

    通过这种方式,如果您的组件重新渲染并再次调用useEffect钩子,则会清除当前超时,并终止addLetterWithDelay方法。

        2
  •  0
  •   FvE    2 年前

    您遇到的问题是由于React的状态更新与setTimeout函数的工作方式造成的。当您在setTimeout回调内调用setPlaceholder时,React将安排状态更新,但在执行setTimeout回叫之前,组件可能已经用以前的状态重新呈现。这可能会导致意外行为,例如字符串被添加两次。

    要解决此问题,可以使用setPlaceholder的函数形式,它将以前的状态作为参数。这样可以确保您始终根据最新的值更新状态。

    const [placeholder, setPlaceholder] = useState<string>('')
    
    useEffect(() => {
        const str = "How may I help you today?";
        const letters = Array.from(str);
    
        const addLetterWithDelay = (index: number) => {
            if (index < letters.length) {
                setTimeout(() => {
                    setPlaceholder(prevPlaceholder => prevPlaceholder + letters[index]);
                    addLetterWithDelay(index + 1);
                }, 1000); // Delay of 1 second
            }
        };
    
        addLetterWithDelay(0); // Start adding letters from index 0
    }, []);

    通过使用setPlaceholder的函数形式,可以确保每次更新都基于最新状态,从而防止字符串被添加两次的问题。