Reputation: 880
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?
Upvotes: 0
Views: 240
Reputation: 3168
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>
Upvotes: 1