setState not updating state even with callback and handleFunction - ReactJs - javascript

I have a handle function which is called in another component. This passess a value called data, which I want to update into my state.
//basic code
initialState = {
wordCount:"Some Value"
text: "Some value"
}
class Home extends Component{
state = initialState;
handler(data){
console.log('Arrived',data)
this.setState({wordCount:data,text:GetText(data,this.state)});
this.validation();
}
validation(){
console.log(this.state.wordCount)
}
render(){
return (
<div><Component handlerFunc={this.handler.bind(this)}</div>
)
}
Printing out data in my handler function I do receive the right information as a string. The I use setState to replace wordCount's initial value with data. I do the same with text. GetText returns a string.
But when validation is run, my state is exactly the same. How could this be?

Note: setState in react is asynchronous.
This means there is no guarantee that state will have changed by the next statement. What you can do to avoid this is setState accepts callback which will be called when set state finishes.
this.setState(
{
wordCount:data,
text:GetText(data,this.state)
},
() => {
// Gets called when setState updates the state
this.validation()
});

Related

React prop nested value is incorrect in event handler

I have a component which passes an object as a prop to its children, as well as a function to update this object.
Now, I've noticed in one of the children that reading a nested property of the prop (obj.nested.property) object always returns the initial value as it is on mount, although the prop (obj) is updated successfully - I can see this in react dev tools, and also from the useEffect console.log
Here's the structure:
const Parent = () => {
const obj = useSelector(config);
const setObj = (newObj) => {
// update obj code
}
return (
<Child obj={obj} onUpdate={setObj}/>
)
}
const Child = ({ obj, onUpdate }) => {
useEffect(() => {
console.log(obj.nested.property);
// here everything is correct. Every time the obj is updated, this logs the new
// obj.nested.property value
}, [obj])
const onBlur = (newValue) => {
if (newValue !== obj.nested.property) {
// here, after you change the value once, and then just click inside the input
// and blur, this condition is always true, because obj.nested.property keeps the
// value as it was on mount
onUpdate(newValue)
}
}
return (
<Input value={obj.nested.property} onBlur={onBlur}/>
)
}
Did you try to make a useState with that object?
const Child = ({ propObj as obj, onUpdate }) => {
const [obj,setObj]= useState(propObj);
useEffect(() => {
console.log(obj.nested.property);
// here everything is correct. Every time the obj is updated, this logs the new
// obj.nested.property value
}, [obj])
const onBlur = (newValue) => {
if (newValue !== obj.nested.property) {
// here, after you change the value once, and then just click inside the input
// and blur, this condition is always true, because obj.nested.property keeps the
// value as it was on mount
onUpdate(newValue)
}
}
return (
<Input value={obj.nested.property} onBlur={onBlur}/>
)
}
According to react documentation, react doesn't update state immediately after calling setState function(because setState is async). new value will set in state after re-rendering the component(here is Child re-render).
React documentation says:
setState() does not immediately mutate state but creates a pending state transition. Accessing state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.
therefore, immediately after calling component setState function and updating state(before re-rendering of component), the value of state is previous value. but in useEffect that calls after re-rendering of component, state has new value.
You can see this for more info.

Mystery Parameter in setState, why does it work?

Going through a TypeScript + React course and building a todo list. My question is about a react feature though.
In the handler for adding a Todo, there is this function declared in setState
const App: React.FC= () => {
const [todos, setTodos] = useState<Todo[]>([])
const todoAddHandler = (text: string) => {
// when its called.... where does the prevTodos state come from?
setTodos(prevTodos => [...prevTodos,
{id: Math.random().toString(), text: text}])
}
return (
<div className="App">
<NewTodo onAddTodo={todoAddHandler}/>
<TodoList items={todos}></TodoList>
</div>
);
}
export default App;
When the function is called in setState, it automatically calls the current state with it. Is this just a feature of setState? That if you declare a function within it the parameter will always be the current state when the function is called?
Was very confused when this parameter just... worked. :#
TL;DR Is this just a feature of setState? - Yes
useState is a new way to use the exact same capabilities that this.state provides in a class
Meaning that its core still relies on old this.setState({}) functionality. If you remember using this.setState(), you will know that it has a callback function available, which can be used like this:
this.setState((currentState) => { /* do something with current state */ })
This has now been transfered to useState hook's second destructured item [item, setItem] setItem, thus it has the same capability:
setItem((currentState) => { /* do something with current state */ })
You can read more about it here
With hooks, React contains an internal mapping of each state name to its current value. With
const [todos, setTodos] = useState<Todo[]>([])
Whenever setTodos is called and todos state is set again, React will update the internal state for todos to the new value. It will also return the current internal state for a variable when useState is called.
You could think of it a bit like this:
// React internals
let internalState;
const setState = (param) => {
if (typeof param !== 'function') {
internalState = param;
} else {
param(internalState);
}
};
const useState = initialValue => {
internalState ??= initialValue;
return [internalState, setState];
}
Then, when you call the state setter, you can either pass it a plain value (updating internalState), or you can pass it a function that, when invoked, is passed the current internal state as the first parameter.
Note that the prevTodos parameter will contain the current state including intermediate updates. Eg, if you call setTodos twice synchronously before a re-render occurs, you'll need to use the callback form the second time in order to "see" the changes done by the first call of setTodos.

post setState evaluation of state

In a react component, I update state in various ways, but I would like to do an evaluation (call a function) after the state was updated.
When I do the following, secondUpdate() does not access to the updated state, so it is one cycle late:
firstUpdate = e => {
this.setState({ email: e.state.value });
// ... some validation
secondUpdate();
}
secondUpdate() {
const allValid= this.state.aIsValid & this.state.bIsValid & this.state.cIsValid;
this.setState({ allIsValid: allValid });
}
How should I bind secondUpdate() to any or some state update?
You can use setState callback :
this.setState({ email: e.state.value } , () => {
// Will get called once the state is updated
// ... some valdidations
secondUpdate();
});
allIsValid shall not be a state at all. By having states that depend on each other, you risk that states get out of sync, and your logic breaks. Instead, as allIsValid is derived from other states, it can just be calculated based on the state inside render:
render() {
const { email } = this.state;
const allIsValid = email.length > 5 && /*...*/;
// ...
}
You can use componentDidUpdate().
componentDidUpdate(prevProps, prevState){
// code from secondUpdate()
}
Here is official doc
You might like to go through this:
https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous
Because this.props and this.state may be updated asynchronously, you
should not rely on their values for calculating the next state.
To fix it, use a second form of setState() that accepts a function
rather than an object. That function will receive the previous state
as the first argument, and the props at the time the update is applied
as the second argument:
this.setState({
// change the state
}, () => {
//Perform something after the state is updated.
})

console is not showing the updated state value after setState [duplicate]

Ok, i'll try and make this quick because it SHOULD be an easy fix...
I've read a bunch of similar questions, and the answer seems to be quite obvious. Nothing I would ever have to look up in the first place! But... I am having an error that I cannot fathom how to fix or why its happening.
As follows:
class NightlifeTypes extends Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
this.handleOnChange = this.handleOnChange.bind(this);
}
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: event.target.checked});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
More code surrounds this but this is where my problem lies. Should work, right?
I've also tried this:
handleOnChange = (event) => {
if(event.target.className == "barClubLounge") {
this.setState({barClubLounge: !this.state.barClubLounge});
console.log(event.target.checked)
console.log(this.state.barClubLounge)
}
So I have those two console.log()'s, both should be the same. I'm literally setting the state to be the same as the event.target.checked in the line above it!
But it always returns the opposite of what it should.
Same goes for when I use !this.state.barClubLounge; If it starts false, on my first click it remains false, even though whether the checkbox is checked or not is based off of the state!!
It's a crazy paradox and I have no idea whats going on, please help!
Reason is setState is asynchronous, you can't expect the updated state value just after the setState, if you want to check the value use a callback method. Pass a method as callback that will be get executed after the setState complete its task.
Why setState is asynchronous ?
This is because setState alters the state and causes re rendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.
Thus the setState calls are asynchronous as well as batched for better UI experience and performance.
From Doc:
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value. There is no
guarantee of synchronous operation of calls to setState and calls may
be batched for performance gains.
Using callback method with setState:
To check the updated state value just after the setState, use a callback method like this:
setState({ key: value }, () => {
console.log('updated state value', this.state.key)
})
Check this:
class NightlifeTypes extends React.Component {
constructor(props) {
super(props);
this.state = {
barClubLounge: false,
seeTheTown: true,
eventsEntertainment: true,
familyFriendlyOnly: false
}
}
handleOnChange = (event) => { // Arrow function binds `this`
let value = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: value}, () => { //here
console.log(value);
console.log(this.state.barClubLounge);
//both will print same value
});
}
}
render() {
return (
<input className="barClubLounge" type='checkbox' onChange={this.handleOnChange} checked={this.state.barClubLounge}/>
)
}
}
ReactDOM.render(<NightlifeTypes/>, document.getElementById('app'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>
Since setState is a async function. That means after calling setState state variable does not immediately change. So if you want to perform other actions immediately after changing the state you should use callback method of setstate inside your setState update function.
handleOnChange = (event) => {
let inputState = event.target.checked;
if(event.target.className == "barClubLounge") {
this.setState({ barClubLounge: inputState}, () => { //here
console.log(this.state.barClubLounge);
//here you can call other functions which use this state
variable //
});
}
}
This is by-design due to performance considerations. setState in React is a function guaranteed to re-render Component, which is a costly CPU process. As such, its designers wanted to optimize by gathering multiple rendering actions into one, hence setState is asynchronous.

setState is not updating state instantly [duplicate]

This question already has answers here:
Why calling setState method doesn't mutate the state immediately?
(3 answers)
Closed 4 years ago.
I have simple component
class App extends Component {
handleClick() {
let banana = {message: 'banana triggered'};
this.setState({banana});
console.log(this); // banana is set in state!!!!
console.log(this.state); // state is null :O
setTimeout(() => {
console.log(this.state); // banana is set!
}, 5)
}
render() {
const {state, actions} = this.props;
return (
<div>
{this.state && this.state.banana.message} <br />
<button onClick={() => this.handleClick()}>Test</button>
{state.alert.message && <p>{state.alert.message}</p>}
<p onClick={() => actions.alert.success("This is not")}>
This is magic
</p>
</div>
)
};
}
export default connect(
state => (
{
state: {...state}
}
),
dispatch => (
{
actions: {
dispatch: dispatch,
alert: {
success: text => dispatch(alert.success(text))
}
}
}
)
)(App);
problem is what i need to add this.state && in my JSX rendering to check if this.state exists at all, i understand what in JavaScript it's normal, but is not normal in React.js? Should he react to state change instantly? Also what get me confused, is what from two console.logs, first (this) have banana set in state, and second one is empty. How?
Image below:
p.s. there is no such problem with Redux, only local component state
react's docs mention that state updates are asynchronous.
In order to act based on the change of the state, react setState function provides a callback which you can use as follows:
this.setState({banana}, () => {
console.log(this.state);
});
In regards to your comment, the value of the state didn't actually exist when it was printed. the value was calculated only after you clicked the expand arrow in the console see this for more deatils
According to react docs, setState() is asynchronous, and multiple calls during the same cycle may be batched together.
If you check the updated state value, you can add a callback method
this.setState({ banana }, ()=> {
// console.log(this.state);
// Here's the updated state
});
In your case, the first console.log(this) doesn't set the banana. See your code in Sandbox. It looks like first two console logs don't show any state as the initial state is null and after the timeout when the asynchronous call has finished it set the state with banana.

Categories