简体   繁体   English

当 useEffect (React js) 中的依赖项发生变化时如何停止重新启动 for 循环

[英]How to stop restarting for loop when dependency change in useEffect (React js)

I am trying to match pi value from input.我正在尝试匹配输入中的 pi 值。 "pracInput" dependency has been used in useEffect so I can get latest value from input and check. “pracInput”依赖项已在 useEffect 中使用,因此我可以从输入中获取最新值并进行检查。 But the problem is that when I input some value the for loop restart.但问题是,当我输入一些值时,for 循环会重新启动。

if I input 9;如果我输入 9; expected value=14159;期望值=14159; counting: 5;计数:5; progress width: 60;进度宽度:60;

if I input another value 2;如果我输入另一个值 2; expected => value=141592;预期 => 值 = 141592; counting: 6;计数:6; progress width: 72;进度宽度:72;

import React, { useEffect, useState } from "react";

const PiGame = () => {
  const [pracInput, setPracInput] = useState("1415");
  const pi = "141592653589793238462643";
  const [widthText, setWidthText] = useState(0);
  const [counting, setCounting] = useState(0);



  useEffect(() => {
    const runLoop2 = async () => {
      for (let i = 0; i < pracInput.length; i++) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        if (pracInput[i] === pi[i]) {
          console.log(i)
          console.log(true);
          setWidthText((prev) => prev + 12);
          setCounting((prev) => prev + 1);

        } else {
          console.log(false);
          alert('not match')

        }
  }
};
    runLoop2();
  }, [pracInput]);

  const handleChange = (e) => {
const val = e.target.value;

if (/^[0-9]+$/.test(val)) {
  setPracInput(val);
}
 };


  return (
<div>
  <div>
    value: <input
      type="text"
      style={{
        width: "80%",
        marginTop: 100,
        height: 25,
        fontSize:25
      }}
      value={pracInput}
      onChange={handleChange}
    />



        <div style={{ fontSize: 25 }}>counting : {counting}</div>
        <div style={{ backgroundColor: "green", width: widthText, height: 20 }}></div>

        <div>progress width : {widthText}</div>


  </div>
</div>


);
};

export default PiGame;

See your for loop is present inside the useEffect which gets triggered whenever your pracInput gets changed,so everything inside that useEffect will be triggered.Your pracInput is being changed when handleChange is being called,so that means whenevr some input will be provided and if (/^[0-9]+$/.test(val)) { setPracInput(val); }看到你的 for 循环出现在 useEffect 中,只要你的 pracInput 发生变化,它就会被触发,所以 useEffect 里面的所有东西都会被触发。当 handleChange 被调用时,你的 pracInput 正在被改变,所以这意味着 whenevr 将提供一些输入, if (/^[0-9]+$/.test(val)) { setPracInput(val); } if (/^[0-9]+$/.test(val)) { setPracInput(val); } becomes true,your pracInput will change and useEffect will be triggered and hence that for loop will start.And since your for loop requires your changes of pracInput,moving it outside of the useEffect also wont make sense. if (/^[0-9]+$/.test(val)) { setPracInput(val); }变为真,你的 pracInput 将改变并且 useEffect 将被触发,因此 for 循环将开始。并且由于你的 for 循环需要你更改 pracInput,将它移到 useEffect 之外也没有意义。 If you want to just start your for loop once only then remove it from that useEffect and do it like this:如果你只想启动你的 for 循环一次,然后将其从 useEffect 中删除并像这样进行:

useEffect(()=>{
for loop goes here
},[]);

This will ensure that for loop runs just once,that is when the component will render for the first time.这将确保 for 循环只运行一次,即组件第一次渲染时。

Edit: According to your output i think this should work:编辑:根据您的 output,我认为这应该有效:

const [widthText, setWidthText] = useState(0);
  const [counting, setCounting] = useState(0);


useEffect(() => {
const TimeoutId = setTimeout(() => {
      if(pracInput==="1415")
    {
    setCounting(4);
setWidthText(60);
}
    else
    {
    setCounting(pracInput.length+1);
    setWidthText(60+(pracInput.length-4)*12);
    }
    }, 1000);
    return () => clearTimeout(TimeoutId);
    

  }, [pracInput]);




const handleChange = (e) => {
const val = e.target.value;

if (/^[0-9]+$/.test(val)) {
  setPracInput(val);
}
 };

And you can now remove the for loop你现在可以删除 for 循环

According to what I understood from question:根据我从问题中了解到的:

import React, { useEffect, useState } from "react";

const PiGame = () => {
  const [pracInput, setPracInput] = useState("1415");
  const pi = "141592653589793238462643";
  useEffect(() => {
    let subscribed = true;
    const runLoop2 = async () => {
      for (let i = 0; i < pracInput.length; i++) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        if (subscribed) {
         if (pracInput[i] === pi[i]) {
            console.log(i)
            console.log(true);
          } else {
            console.log(false);
          }
        }
  }
};
    runLoop2();

   return () => subscribed = false; // to avoid memory leak on unmount of component


  }, []);. // Passing empty array will make it execute only once, at component mount

  const handleChange = (e) => {
const val = e.target.value;

if (/^[0-9]+$/.test(val)) {
  setPracInput(val);
}
 };


  return (
<div>
  <div>
    <input
      type="text"
      style={{
        width: "80%",
      }}
      value={pracInput}
      onChange={handleChange}
    />

    <div>{pracInput}</div>
  </div>
</div>


);
};

export default PiGame;

As you are doing asynchronous task in useEffect you must use some logic/trick to avoid memory leak.当您在 useEffect 中执行异步任务时,您必须使用一些逻辑/技巧来避免 memory 泄漏。

Are you looking for this?你在找这个吗?

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

const PiGame = () => {
  const [pracInput, setPracInput] = useState("1415");
  const pi = "141592653589793238462643";
  const counter = useRef(4); // store the counter
  useEffect(() => {
    const runLoop2 = async () => {
      for (let i = counter.current; i < pracInput.length; i++) {
        await new Promise((resolve) => setTimeout(resolve, 1000));
        if (pracInput[i] === pi[i]) {
          console.log(i)
          console.log(true);
        } else {
          console.log(false);
        }
      }
      counter.current = counter.current + pracInput.length;
    };
    runLoop2();
  }, [pracInput]);
  // the rest same as before

Because you added "pracInput" in the array.因为你在数组中添加了“pracInput”。 "UseEffect" excutes everytime you call it and therefore will always call the looping function to start over. “UseEffect”在您每次调用它时都会执行,因此将始终调用循环 function 重新开始。 To stop this, you could either remove the "pracInput" from the dependency array and get the value in another way or you could use the "UseState" hook to set a certain value when the loop starts and then base a condition on your loop function call.要停止这种情况,您可以从依赖项数组中删除“pracInput”并以另一种方式获取值,或者您可以使用“UseState”挂钩在循环开始时设置特定值,然后基于循环条件 function称呼。 Something like this像这样的东西

const [cond, setCond] = useState(false);

Set its value to be true when the loop starts then add a condition like this在循环开始时将其值设置为 true 然后添加这样的条件

if(cond == false)
     runLoop2();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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