e.target.value onChange input react - reactjs

I have this simple onChange function, but I have problem. When I change my input first time and for example enter "1" my variable inputOne is returns empty string:
const [inputOne, setInputOne] = useState('');
const onChange = (e) => {
setInputOne(e.target.value);
console.log(e.target.value); // first changing of input returns 1
console.log(inputOne); // first changing of input returns EMPTY string
}
<div className="container">
<input type="number" name='input1' value={inputOne} onChange={onChange} />
</div>
but when I change this input again and add one more "1"(in total 11) my console is:
console.log(e.target.value); // returns 11
console.log(inputOne); // returns 1
Why it's happening with my variable inputOne?
New code:
const [inputOne, setInputOne] = useState('');
useEffect(() => {
console.log(inputOne);
}, [inputOne])
const onChange = (e) => {
setInputOne(e.target.value);
console.log(e.target.value);
setTimeout(() => {
if(e.target.value){
const filteredpost = posts[0].filter(mfo => mfo.minPrice <= Number(inputOne));
setPostsToShow(filteredpost.slice(0, 20));
setPost(filteredpost);
}else{
const filteredpost = posts[0];
setPostsToShow(filteredpost.slice(0, 20));
setPost(filteredpost);
}}, 1000);
}

setState is an async function.
In your case, setInputOne queues the change and returns a Promise,
that will not be resolved until the next tick (or even later, if reacts thinks it is worth it to gain some performance).
So the timeline is like this:
Type into input
Trigger onChange
setInputOne (queue the change)
console.log (the value that is queued)
console.log (the variable that is queued)
next tick and consequently the change of the variable.
You can see this with the useEffect hook:
useEffect(() => {
console.log(`tell me when inputOne changes`);
}, [inputOne])
UPDATE
inputOne will never be your updated value inside the onChange function. The onChange function stores the last value until re-render.
Pass your setTimeout to the useEffect OR change inputOne to e.target.value since they will always be the same.

State update in Reactjs is an asynchronous process, therefore it won't be reflected immediately in the next line due to it's asynchronous nature.
If you want to monitor the state whenever its updated, you can use useEffect hook, and place inside its the dependency array the piece of state you want to track.
In your case:
useEffect(() => {
console.log(inputOne);
}, [inputOne])
This will be triggered, every time inputOne changes. If you want to use the value from the inputOne to call another function you should implement that logic inside the useEffect, instead of doing it inside the function onChange which updates the inputOne state.
useEffect(() => {
if(inputOne){
const filteredpost = posts[0].filter(mfo => mfo.minPrice <= Number(inputOne));
setPostsToShow(filteredpost.slice(0, 20));
setPost(filteredpost);
}
else
{
const filteredpost = posts[0];
setPostsToShow(filteredpost.slice(0, 20));
setPost(filteredpost);
}
}, [inputOne]);
Get rid of the timeout. It's unnecessary.

Related

How to checddsa hooks?

I need to detect every change of variable state BUT check code:
useEffect(() => {
if (value) {
setSelect(value);
}
}, [value]);
code above checkin every second and i got error:
Warning: Maximum update depth exceeded. This can happen when a
component calls setState inside useEffect, but useEffect either
doesn't have a dependency array, or one of the dependencies changes on
every render.
I need to check only when value is changed not every second!
value is props data from parent component.
const OrganisationUnitInput = ({
input
}) => {
const [showModal, setShowModal] = useState(false);
const value = input.value || [];
const value = input.value || [];
const handleChange = useCallback(
(value) => {
input.onChange(value);
setShowModal(false);
},
[input]
);
}
It is happening because you are setting value state and passing same value as dependency array . which results in re rendering
You misunderstood the useEffect hook, when you pass the second argument with some variable you are telling to react that the function of the effect must be executed if the variable change.
So in your code You are making an infinity loop.
To detect the change on the var only pass the var in the array and your function will be executed if any change occurred.
const Comp = () => {
const [value, setValue] = useState(0)
useEffect(() => {
// This code will be execute any time that the var value
// change
console.log("The value change, now is: ", value)
// so if you change the var here your will cause another call
// to this effect.
}, [value])
return <button onClick={() => { setValue(prev => prev + 1) }}>
Change Value
</button>
}

does setInterval inside a useEffect can recognize an updated state?

I'm working on an app which requires a user location every X seconds. When the component is mounted, the interval starts and fetch the GPS location and compare it to the old one which is saved on the component state.
The thing is, the interval is comparing only to the default state of the variable, so if the new value is different than the old one, a setter is called to change that value.
I used useEffect on the location var so whenever it changes, to just print it and it does correctly by the new value.
but the interval keeps using the default value given in useState hook.
What is it that I'm missing here.
An example of my code is below:
const [currentLocation, setCurrentLocation] = useState(null);
let locationInterval = null;
useEffect(() => {
locationInterval = setInterval(async () => {
console.log("IN INTERVAL");
navigator.geolocation.getCurrentPosition((location, error) => {
if (location) {
location = [location.coords.latitude, location.coords.longitude];
/// currentLocation is not updating here for some reason
if (JSON.stringify(location) !== JSON.stringify(currentLocation)) {
alert(
`the new location in interval ${location} old location: ${currentLocation}`
);
setCurrentLocation(location);
}
} else {
console.log(error);
}
});
}, 15000);
}, [map]);
useEffect(() => {
return () => {
clearInterval(locationInterval);
};
}, []);
useEffect(() => {
/// currentLocation is updated here from the setInterval
console.log("newlocation", currentLocation);
}, [currentLocation]);
The reason is because currentLocation value is not given as a dependency in the useEffect, therefore that useEffect callback function only has access to the original useState value.
May I ask are there any React warning on your terminal where you run your react app?
Something like
React Hook useEffect has a missing dependency: 'currentLocation'
But on the side node, if you add currentLocation to the dependency,since because you are updating currentLocation in your useEffect and if it's updated, it will re-run the useEffect callback again.
You can not get an updated state in setInterval because callback inside useEffect hook only called when its dependency array updates([map]).
When it is called currentLocation is passed as a parameter to setInterval's callback. Which is the default value for useState at that time.
To prevent this you can do this.
const [currentLocationRef] = useRef(null);
useEffect(() => {
locationInterval = setInterval(async () => {
//....
if (JSON.stringify(location) !== JSON.stringify(currentLocationRef.current)) {
currentLocationRef.current = location;
}
}, 15000);
return () => {
clearInterval(locationInterval);
};
}, [map, currentLocationRef]);
Also, you should return clearInterval in the same useEffect Callback.
The reason behind this is currentLocationRef.current is different from currentLocation. The first one is the getter function and the second one is value. That's why the first one is still able to access updated value while the second one cannot.

How do I avoid trigger useEffect when I set the same state value?

I use react hook call an API and set the data to state and I still have some control view can change the value, but when I change it, my API function trigger again, it cause my view re render multiple times.
How do I use my fetchData function just like in class component componentDidMount function ?
const [brightness, setBrightness] = useState(0);
useEffect(() => {
fetchData();
});
const fetchData = async () => {
const value = await something(); // call API get the first value
setBrightness(value);
};
return (
<View>
<SomeView value={brightness} onComplete={(value) => setBrightness(value)}
</View>
);
Your useEffect will be triggered on every render because you haven't provided the 2nd argument.
The 2nd argument is what the effect is conditional upon. In your case you only want it to run once so you can provide an empty array.
useEffect(() => {
// Run once
}, [])
Lets say you wanted it to fetch anytime some prop changed, you could write
useEffect(() => {
// run every time props.myValue changes
}, [props.myValue])
See https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

How do you deal with outside data in a React event listener?

In an event handler in React, how do you get and set outside data?
Here's an example that counts keyup events. Because the function is only created once, the listener always sees the keyup count as what it was initially, and it will never count above 1.
Working JSFiddle, or see code sample below.
function CountKeypresses() {
const [keypressCount, setKeypressCount] = React.useState(0);
const handleKeyup = (event) => {
setKeypressCount(keypressCount + 1); //keypressCount is always 0 here no matter what
}
//Only create the event listener once
React.useEffect(() => {
window.addEventListener("keydown", handleKeyup);
}, []);
return keypressCount;
}
ReactDOM.render(<CountKeypresses />, document.querySelector("#app"))
Use the callback form of the setter so that you can set the new value to the current previous value plus 1:
setKeypressCount(keypressCount => keypressCount + 1);
Another option is to add and remove the listener every time keypressCount changes, though it's a bit uglier:
React.useEffect(() => {
window.addEventListener("keydown", handleKeyup);
return () => window.removeEventListener("keydown", handleKeyup);
}, [keypressCount]);
If you're in a similar situation where you don't want to set state, but you need to get the current value in state, and the useEffect approach above isn't suitable, you can also store the value in a ref instead, but such situations are somewhat unusual in my experience.

How to correctly use React `useCallback`'s dependencies list?

I have an example like this:
codesandebox
I want to modify a state value in a callback, then use the new state value to modify another state.
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("0");
const [added, setAdded] = useState(false);
const aNotWorkingHandler = useCallback(
e => {
console.log("clicked");
setCount(a => ++a);
setText(count.toString());
},
[count, setCount, setText]
);
const btnRef = useRef(null);
useEffect(() => {
if (!added && btnRef.current) {
btnRef.current.addEventListener("click", aNotWorkingHandler);
setAdded(true);
}
}, [added, aNotWorkingHandler]);
return <button ref={btnRef}> + 1 </button>
However, after this handler got called, count has been successfully increased, but text hasn't.
Can you guys help me to understand why this happened? and how to avoid it cleanly?
Thank you!
If count and state are always supposed to be in lockstep, just with one being a number and one being a string, then i think it's a mistake to have two state variables. Instead, just have one, and derive the other value from it:
const [count, setCount] = useState(0);
const text = "" + count;
const [added, setAdded] = useState(false);
const aNotWorkingHandler = useCallback(
e => {
setCount(a => ++a);
},
[]
);
In the above useCallback, i have an empty dependency array. This is because the only thing that's being used in the callback is setCount. React guarantees that state setters have stable references, so it's impossible for setCount to change, and thus no need to list it as a dependency.
There are few things causing the issue.
Setter does not update the count value immediately. Instead it "schedules" the component to re-render with the new count value returned from the useState hook. When the setText setter is called, the count is not updated yet, because the component didn't have chance to re-render in the mean time. It will happen some time after the handler is finished.
setCount(a => ++a); // <-- this updates the count after re-render
setText(count.toString()); // <-- count is not incremented here yet
You are calling addEventListener only once and it remembers the first value of count. It is good you have aNotWorkingHandler in the dependencies - the onEffect is being re-run when new count and thus new handler function comes. But your added flag prevents the addEventListener from being called then. The button stores only the first version of the handler function. The one with count === 0 closured in it.
useEffect(() => {
if (!added && btnRef.current) { // <-- this prevents the event handler from being updated
btnRef.current.addEventListener("click", aNotWorkingHandler); // <-- this is called only once with the first instance of aNotWorkingHandler
setAdded(true);
} else {
console.log("New event handler arrived, but we ignored it.");
}
}, [added, aNotWorkingHandler]); // <-- this correctly causes the effect to re-run when the callback changes
Just removing the added flag would, of course, cause all the handlers to pile up. Instead, just use onClick which correctly adds and removes the event handler for you.
<button onClick={aNotWorkingHandler} />
In order to update a value based on another value, I'd probably use something like this (but it smells of infinite loop to me):
useEffect(
() => {
setText(count.toString());
},
[count]
);
Or compute the value first, then update the states:
const aNotWorkingHandler = useCallback(
(e) => {
const newCount = count + 1;
setCount(newCount);
setText(newCount.toString());
},
[count]
);
I agree with #nicholas-tower, that if the other value does not need to be explicitly set and is always computed from the first one, it should be just computed as the component re-renders. I think his answer is correct, hope this context answers it for other people getting here.

Resources