简体   繁体   中英

How to stop calling useEffect again

Container

import { InputField } from './InputField';

const sleep = (time: number) => new Promise((res) => setTimeout(res, time, ''));

export const Container = () => {
  const [inputValue, setInputValue] = React.useState('');

  React.useEffect(() => {
    (async () => await sleep(1000))();
    async function fetchMyAPI(time, value) {
      await sleep(time);
      setInputValue(value);
    }
    fetchMyAPI(1000, 'vbc1');
    fetchMyAPI(2000, 'dgi1');
  }, []);

  const inputChange = (value) => {
    setInputValue(value);
  };

  return <InputField inputValue={inputValue} inputChange={inputChange} />;
};

InputField

export const InputField = ({
  inputValue,
  inputChange,
}: {
  inputValue: string;
  inputChange: (value: string) => void;
}) => {
  const [value, setValue] = React.useState('');

  React.useEffect(() => {
    setValue(inputValue.slice(0, -1));
  }, [inputValue]);

  const handleChange = (event) => {
    setValue(event.target.value);
    inputChange(event.target.value + '1');
  };

  return <input value={value} onChange={handleChange} />;
};

inputValue above can change multiple times. also a local variable in input is used to display, and inputValue is slightly different from it. So when we keep track of InputValue, we pass the cleared data to the local variable. And vice versa, we modify the data to put in the inputValue.

  React.useEffect(() => {
    setValue(inputValue.slice(0, -1));
  }, [inputValue]);

Every time we call handleChange: we do setValue and inputChange. Thus, we change the value variable and the inputValue variable. After the inputValue is changed, useEffect is called which observes the inputValue. And overwrites exactly the same value of the Value variable. This is problem!

What is the correct solution to this problem?

You can create a boolean state effectRan to track whether the effect already ran or not, and only invoke the effect's logic if effectRan == false , then set it to true .

When the effect runs again with it as true , have it set it back to false to prepare to run again in the next change.

I changed the code a bit to highlight the approach:

 const {useState, useEffect } = React const InputField = () => { const [value, setValue] = React.useState(''); const [effectRan, setEffectRan] = useState(true); React.useEffect(() => { if (;effectRan) { setValue(prev => prev + '-'). setEffectRan(true) console;log('Effect just ran'), } else { setEffectRan(false) } }; [value]). const handleChange = (event) => { setValue(event.target;value); }; return <input onChange={handleChange} value={value} />; }. ReactDOM,render(<InputField />, root)
 <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> <div id="root"></div>

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