Add element to array setState React - javascript

I have useState "setAnswers" (set) and "answers" (get) (answers is array with strings)
and click trigger:
onClick = () => {
setAnswers((prev) => [...prev, e])
setValue(questionKey, answers)
console.log(watch(questionKey))
}
but with ever click i got only previous value

In fact, your console.log is execute before the state finish to be calculated, if you put your console.log on an other place, normally, you find what you want.
Try it, and say me

Your console.log(watch(questionKey)) all time will go wrong on onChange.
you need use a useEffect to log or make anything else with state as base.
useEffect(() => {
console.log(watch(questionKey)
}, [questionKey]);
to more info you can read here:
https://dev.to/mpodlasin/react-useeffect-hook-explained-in-depth-on-a-simple-example-19ec

I think you are a little bit confused: watch function from useForm is used to
Watch specified inputs and return their values.
So console.log(watch(questionKey)) does make no sense.
watch should be used in this way:
React.useEffect(() => {
const subscription = watch((value, { name, type }) => console.log(value, name, type));
return () => subscription.unsubscribe();
}, [watch]);
You use a useEffect to subscribe/unsubscribe watch on some form fields, and then in component's body you could call:
const watchSomething = watch(<something>);
and, every time the field called <something> will change his value, watch will execute console.log(value, name, type).
If you want to print the very last value of a state variable, you should know that setValue, setAnswer are async functions and its absolutely not guaranteed that on next line you could log the last value.
To solve your problem you have 2 choices:
use watch correctly;
forget watch and use classic useEffect:
onClick = () => {
setAnswers((prev) => [...prev, e])
setValue(questionKey, answers)
}
useEffect(() => {
console.log(questionKey); //<-- here you will print the very last value of questionKey
}, [questionKey]);
Here a guide on how to use watch.

Related

Filter Inside UseEffect React Not Working

Im using the .filter() method on an object inside of a useEffect() method to filter out certain arrays out by name if they exist in a second object. I need to get the difference of arrays back into a useState() method. Im using the following and works outside the useEffect() method:
useEffect(() => {
getDBData().then( (r) => { setAnotherObj(r); });
getAPICall().then((r) => {
let result = r.filter(
(o1) => !anotherObj.filter((o2) => o1.name === o2.name)
);
setOption(result);
});
}, []);
Now that works outside of the useEffect method when I add it to an event like onClick, but not inside, it might work one time then it doesn't at all. What am I missing about the useEffect method that I need to know why the filtering isn't being done?
Replace second filter with find.
!anotherObj.filter(o2 => o1.name === o2.name) will always return false, be it has elements or not.
There is no dependency array in the in the useEffect, so whenever there is a state change, this useEffect gets triggered again.
Finding A-B, filter all the elements of A that are not in B.
r.filter(o1 => anotherObj.find(o2 => o1.name !== o2.name)); With this it removes all the elements that are common in A and B. And leaves out only elements in A.
Update as follows,
useEffect( () => {
getAPICall().then( (r) => {
const result = r.filter(o1 => anotherObj.find(o2 => o1.name !== o2.name));
setOption(result);
});
}, []);

React state updating but rendering late

I've tried almost every solution similar to my problem, yet none is working. I have a simple state and changing the value of this state in a function as like below, handleOnClick is calling in a button's onClick event. I'm also using Router(if it's change something);
import { useState} from "react"
import { BrowserRouter as Router, Route, Link, useHistory} from "react-router-dom";
const Buton = () => {
let x = "";
const [lowerState, setLower] = useState("")
const history = useHistory();
const handleOnClick = () => {
x = document.getElementById("my_input").value.toLowerCase();
setLower(x)
console.log(x) //this prints the current value
console.log(lowerState) //this DOES NOT prints the current value, but
// when I put another text into the input and click
// to button, it prints the first value I put here
history.push('/test', {params : lowerState})
};
.
.
.
return (...)
}
export default Buton
Now x is a value that returns from an input HTML element. When I set this value as a state and console log, it doesn't print the value first, when I put something in input again, then it prints the first value. So it's like it's coming 1 step behind.
I've used useEffect() , I did put a second parameter to setLower as console.log(lowerState) and other things on the internet that people suggested, but none is working. Every time, the state is coming 1 step behind. How can I make this state changes immediately?
If you want to use the value of an input in a user event function, the best way (and least buggy) is to bind your input value to local state and then just reference that state in your callback function.
Please try to avoid imperatively pulling values from the DOM using getElementById etc. Here's what I mean:
const [value, setValue] = useState('');
// This will keep everything updated until you need to use it
handleChange(event) {
setValue(event.target.value);
}
// Then just grab whatever is in local state
handleClick() {
history.push('/test', {params : value});
}
return (
<input value={value} onChange={handleChange} />
// Your button is here too
)
This is because when you call setLower(x) it is not an async call. So it doesn't wait. That's why you get the 1 step before value in your state right after setting the value.
Official doc - https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
When you call setLower(x), it doesn't immediately update the lowerState. The new value will be available the next time it renders. Because of that the console.log(x) "works" (since it uses the new value that you gain as a parameter) but console.log(lowerState) uses the old value that hasn't updated to the state yet at that point.
If you want history.push('/test', {params : lowerState}) to work, then you need to use the x there instead of lowerState. Or call it within a useEffect with the lowerState and having lowerState as a dependency parameter to the hook.
This is expected behaviour since React is updating state in a batch
Which mean that the state only gets an update after an eventHandler/function is finished
If you want to do some condition, wrap your logic inside a useEffect
useEffect(() => {
if (lowerState === "your-condition-value") {
history.push("/test", { params: lowerState });
}
}, [lowerState]);
Or in your case, just use the variable directly:
const handleOnClick = () => {
x = document.getElementById("my_input").value.toLowerCase();
history.push("/test", { params: x });
};
You should not worry about that since your app still working as expected
So i would like to suggest that use useRef if need for reference only object which may not causing rerendering. also using let x= "" is not correct, you should write code immutable way
const Buton = () => {
const lowerCaseRef = useRef("")
const history = useHistory();
const handleOnClick = () => {
lowerCaseRef.current =
document.querySelector("#my_input").value.toLowerCase();
console.log(lowerCaseRef.current) //this DOES NOT prints the current value, but
// when I put another text into the input and click
// to button, it prints the first value I put here
history.push('/test', {params : lowerCaseRef.current})
};
return (...)
}

setState in functional component don't keep value

I have a functional component that keeps a state. This state I try to manipulate using an onClick event in an SVG. The SVG is in another component and has the addAndRemoveSelectedCabin method passed to it via props. I loop through the elements in an useEffect and add an eventListener. This doesn't work. The useEffect with the selectedCabins dependency logs the new number only. It seems the state returns to the initial state after every stateChange.
This is the state and method in the parent component.
const [selectedCabins, setSelectedCabins] = useState([0]);
const addRemoveSelectedCabin = id => {
const newArr = [...selectedCabins, id];
setSelectedCabins(newArr);
}
useEffect(() => {
console.log(selectedCabins);
}, [selectedCabins])
This is how I call the method. [UPDATE]
useEffect(() =>
{
const cabins = document.querySelectorAll(".cabin");
cabins.forEach(cabin =>
{
const id = cabin.getAttributeNS(null, "id").substring(1, 5);
const found = cabinsData.find(el => el.id === id)
if (found && found.status === "available")
{
cabin.classList.add("green")
cabin.addEventListener('click', () => addRemoveSelectedCabin(id));
} else if (found && found.status === "booked")
{
cabin.classList.add("gray")
}
})
}, [])
Console:
[0]
(2) [0, "1105"]
(2) [0, "1101"]
This works if I put the onClick directly in the SVG element. Does anyone know why this is?
<rect
id="C1105"
x="749.4"
y="58.3"
className="cabin"
width="36.4"
height="19.9"
onClick={() => addRemoveSelectedCabin(1105)}
>
<title>1105</title>
</rect>
As I said in my comment, you are binding addRemoveSelectedCabin in the first render. useEffect is only executed once since you pass an empty dependency list. addRemoveSelectedCabin closes over selectedCabins which at that point in time has the value [0].
Why am I seeing stale props or state inside my function? from the React documentation has more information about this.
The solution in your case is simple: Pass a function to the setter to get the "current" state value. Don't reference the state value in the component:
const addRemoveSelectedCabin = id => {
setSelectedCabins(selectedCabins => [...selectedCabins, id]);
}
Having said that, this is still an odd thing to do in React world. You should reevaluate your assumptions that make you think you have to do it that way.
It's not all the elements that should have a click listener.
Depending on how you actually render the elements, that's easy to do. JSX/React is just JavaScript. Whether you have a condition that adds the event handler or not or whether you have a condition that sets onClick or not is basically the same.
But without a more complete example there is not much we can suggest.

why it is not showing update value on button click from state?

could you please tell me how to get updated value from state.here is my code
https://codesandbox.io/s/cool-ives-0t3yk
my initial state
const initialState = {
userDetail: {}
};
I enter 10 digit number on input field and press enter and update the user detail like this
const onSubmit = async values => {
if (values.mobile.length === 10) {
setUserDetail({ msdin: values.mobile });
console.log(userDetail);
}
};
setUserDetail({ msdin: values.mobile }); here I am updating my userdetail .
and try to console the update value like this
console.log(userDetail); .it is showing currently undefined.but expected output is {msdin:'9999999999'} (or whatever it is type in input field)
The problem is that you are using hooks and it's not synchronised, it's async. Therefore, accessing the detail immediately after setting the value will not be possible. If you want to access the data there, you will have to use values.mobile
The state will keep the last value until the next render is called.
You can see this information on react-hooks document
During subsequent re-renders, the first value returned by useState will always be the most recent state after applying updates.
So, the code should look like:
const onSubmit = async values => {
if (values.mobile.length === 10) {
const newUserDetailState = { msdin: values.mobile }
setUserDetail(newUserDetailState);
// do your stuffs with the newUserDetailState instead of userDetail
console.log(newUserDetailState);
}
};
The state setter setUserDetail is async, that means that the new state value won't be available immediately.
To see if the state update use useEffect like this :
useEffect(() => {
console.log('useEffect -> UserDetail : ', userDetail);
}, [userDetail]);

Looking for assistance in getting proper results from .filter()

I am trying to filter an array with a string that is input by user. The results are not updating properly with the first key input, then if the box is cleared or characters removed/changed, results that may now pass the filter are not being displayed.
The goal is to have all results displayed on initial page render, then properly updated with each keystroke.
Apologies; I'm just learning to code. Thanks for all assistance.
searchCompUsers = () => {
const newState = {}
const filteredEmps = this.props.employees.filter(
user => user.name.includes(this.state.searchName)
)
console.log(filteredEmps)
`` newState.filterEmps = filteredEmps
this.setState(newState)
}
empSearch = evt => {
const stateToChange = {};
stateToChange[evt.target.id] = evt.target.value;
this.setState(stateToChange);
this.searchCompUsers()
};
These lines are being run in sequence:
this.setState(stateToChange);
this.searchCompUsers();
...
const filteredEmps = this.props.employees.filter(
user => user.name.includes(this.state.searchName)
)
...
this.setState(newState);
I am assuming in your example, evt.target.id is searchName.
Two things you're doing here which you shouldn't do:
Running two setStates in sequence. This isn't necessarily a problem, but there's generally no reason for it and it could mean your code is structured poorly.
Referencing the state immediately after setState. setState is run asynchronously, so you can't guarantee the state will be updated by the time you reach your filter.
The weird results you're getting are probably stemming from (2).
Something like this would work better, assuming the rest of your code is fine:
empSearch = evt => {
const key = evt.target.id;
const value = evt.target.value;
if (key === "searchName") {
const filteredEmps = this.props.employees.filter(
user => user.name.includes(value);
);
this.setState({
filterEmps: filteredEmps
});
}
};
This way, you're only calling setState once per event, and you're not relying on the results of an earlier setState.
If you need to keep searchName in the state for some reason (such as using a controlled component), then you can simply add it to the same setState.
this.setState({
filterEmps: filteredEmps,
searchName: value
});
The only places you can assume the state is up-to-date is in the render() function, and in certain React lifecycle functions. You can also provide a callback to setState if necessary, though this should be relatively rare: this.setState({ ...someState }, () => { ...someCodeToRun() });

Categories