How to get the current onChange value in React? [duplicate] - javascript

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.

Related

Pass params from state to a checkbox tree

I have this code :
constructor(props) {
super(props);
this.dispatch = props.dispatch;
this.state = {
checked: [],
expanded: [],
};
const {dispatch, match, t} = this.props;
this.onCheck = this.onCheck.bind(this);
this.onExpand = this.onExpand.bind(this);
}
onCheck(checked) {
this.setState({ checked });
console.log(this.state.checked);
this.loadProducts(this.state.checked);
}
onExpand(expanded) {
this.setState({ expanded });
}
render() {
const { checked, expanded } = this.state;
console.log(checked);
........
<CheckboxTree
nodes={this.props.chosenCategory.children}
checked={checked}
expanded={expanded}
onCheck={this.onCheck}
onExpand={this.onExpand}
.......
/>
The problem is that in console.log from render function I have the correct checked value. But in the function onCheck checked have the previous value. I don't understand what's the problem. Can you help me please ?
Due to the asynchronous behaviour of setState, we can't guarantee that the following line will have the updated value, at that particular instance.
In your case, if you want to log the updated value inside onCheck, the you can use the callback provided by the setState function.
onCheck(checked) {
this.setState({ checked }, () => {
this.loadProducts(this.state.checked); //param - checked will also do
console.log(this.state.checked);
});
}
now the console.log will execute, after successful setState.
There are 2 types of setState, the first one takes an object as a parameter, and the second one takes a function as a parameter. The difference between the two is the first one is asynchronous and the second is synchronous.
So you need to use setState with function as a parameter.
onCheck(checked) {
this.setState((state) => ({ checked}));
console.log(this.state.checked);
this.loadProducts(this.state.checked);
}

React re-render conditional on state change without changing the conditional

I've got a conditional that displays an editor while a certain prop remains true. The thing is, the data with which that editor is rendered with should change every time I select another object with which to populate that editor.
However, because the prop responsible for the conditional rendering doesn't change, even though the data with which the editor is rendered does, it refuses to re-render on state change.
I'm not particularly good at React, so, hopefully someone can explain how I can get around this little hiccup.
Conditional render
{this.state.showEditor ? (<BlockEditor routine={this.state.editorObject} />) : null}
Method that is being called.
handleShowEditor = routine => {
this.setState({ showEditor: true });
this.setState({ editorObject: routine });
};
The editor component
export default class BlockEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
routine: this.props.routine
};
}
render() {
return (
<div>
<Editor
autofocus
holderId="editorjs-container"
onChange={data => this.handleSave(data)}
customTools={{}}
onReady={() => console.log("Start!")}
data={this.props.routine.description}
instanceRef={instance => (this.editorInstance = instance)}
/>
</div>
);
}
}
Is there a reason for setting state separately? Why not
handleShowEditor = routine => {
this.setState({
showEditor: true,
editorObject: routine
});
};
Keep in mind that setState is asynchronous and your implementation could lead to such weird behaviour.
If you are still looking for an answer i have faced the same problem working with the same [Editor.JS][1] :).
This worked for me with functional component:
// on change fires when component re-intialize
onChange={async (e) => {
const newData = await e.saver.save();
setEditorData((prevData) => {
console.log(prevData.blocks);
console.log(newData.blocks);
if (
JSON.stringify(prevData.blocks) === JSON.stringify(newData.blocks)
) {
console.log("no data changed");
return prevData;
} else {
console.log("data changed");
return newData;
}
});
}}
// setting true to re-render when currentPage data change
enableReInitialize={true}
Here we are just checking if data changes assign it to editorData component state and perform re-render else assign prevData as it is which will not cause re-render.
Hope it helps.
Edit:
i am comparing editor data blocks change which is array.
of course you need to perform comparison of blocks more deeply than what i am doing, you can use lodash for example.
[1]: https://github.com/editor-js/awesome-editorjs
As setState is asynchronous you can make another call in its callback.
Try like this
handleShowEditor = routine => {
this.setState({
showEditor: true
}, () =>{
this.setState({
editorObject: routine
)}
});
};

How to update a state right after the setState in react? [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.

State is not updating in reactjs [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.

Is there a synchronous alternative of setState() in Reactjs

According to the explaination in the docs:
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.
So since setState() is asyncronous and there is no guarantee about its synchronous performance. Is there an alternative of setState() that is syncronous.
For example
//initial value of cnt:0
this.setState({cnt:this.state.cnt+1})
alert(this.state.cnt); //alert value:0
Since the alert value is previous value so what is the alternative that will give alert value:1 using setState().
There are few questions on Stackoverflow which is similar to this question but no where I am able to find the correct answer.
As you have read from the documentation, there is NO sync alternative, reason as described is performance gains.
However I presume you want to perform an action after you have changed your state, you can achieve this via:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
x: 1
};
console.log('initial state', this.state);
}
updateState = () => {
console.log('changing state');
this.setState({
x: 2
},() => { console.log('new state', this.state); })
}
render() {
return (
<div>
<button onClick={this.updateState}>Change state</button>
</div>
);
}
}
ReactDOM.render(
<MyComponent />,
document.getElementById("react")
);
<div id="react"></div>
<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>
You could wrap setState in a function returning a promise, and then use this function with the await keyword to cause your code to wait until the state has been applied.
Personally, I would never do this in real code, instead I would just put the code I wish to execute after the state update in the setState callback.
Nevertheless, here is an example.
class MyComponent extends React.Component {
function setStateSynchronous(stateUpdate) {
return new Promise(resolve => {
this.setState(stateUpdate, () => resolve());
});
}
async function foo() {
// state.count has value of 0
await setStateSynchronous(state => ({count: state.count+1}));
// execution will only resume here once state has been applied
console.log(this.state.count); // output will be 1
}
}
In the foo function, the await keyword causes the code execution to pause until the promise returned by setStateSynchronous has been resolved, which only happens once the callback passed to setState is called, which only happens when the state has been applied. So execution only reaches the console.log call once the state update has been applied.
docs for async/await:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await
If this is required I would suggest using a callback in your setState function (and I also suggest using a functional setState).
The callback will be called after the state has been updated.
For example, your example would be
//initial value of cnt:0
this.setState(
(state) => ({cnt: state.cnt+1}),
() => { alert(this.state.cnt)}
)
as per documentation here : https://facebook.github.io/react/docs/react-component.html#setstate
Note: Official docs say, "Generally we recommend using componentDidUpdate() for such logic instead."
No, there is not. React will update the state when it sees fit, doing things such as batching setState calls together for efficiency. It may interest you that you are able to pass a function into setState instead, which takes the previous state, so you may choose your new state with good knowledge of the previous one.
You can use flushSync from ReactDOM to update the state synchronously as suggested by react docs.
https://reactjs.org/docs/react-component.html
It may sound weird but yes setState can work synchronously in react.
How so? This is POC which I've created to demonstrate it.
Pasting the only app JS code.
Maybe it's possible that I'm missing something but this was actually happening in my application that's when I came to know about this effect.
Correct me if this kind of behavior is expected in React which I'm unaware of.
When there are multiple setState on main thread the setState runs a Batch combining all the setState on the main method. Whereas the Scenario is different when the same things go inside the async Function.
import React, { Component } from 'react';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
}
this.asyncMethod = this.asyncMethod.bind(this);
this.syncMethod = this.syncMethod.bind(this);
}
asyncMethod() {
console.log("*************************")
console.log("This is a async Method ..!!")
this.setState({
counter: this.state.counter + 1
}, () => {
console.log("This is a async Method callback of setState. value of counter is---", this.state.counter);
})
console.log("This is a async Method on main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
syncMethod() {
var that = this;
console.log("*************************")
console.log("This is a sync Method ..!!")
that.setState({counter: "This value will never be seen or printed and render will not be called"});
that.setState({counter: "This is the value which will be seen in render and render will be called"});
setTimeout(() => {
that.setState({counter: "This is part is synchronous. Inside the async function after this render will be called"});
console.log("setTimeout setState");
that.setState({counter: "This is part is aslso synchronous. Inside the async function after this render will be called"});
}, 10)
console.log("This is a sync Method on Main thread. value of counter is---", this.state.counter);
console.log("*************************")
}
render() {
console.log("Render..!!",this.state.counter);
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
</header>
<button onClick={this.asyncMethod}>AsyncMethod</button>
<button onClick={this.syncMethod}>SyncMethod</button>
</div>
);
}
}
export default App;
Use React Hooks instead:
function MyComponent() {
const [cnt, setCnt] = useState(0)
const updateState = () => {
setCnt(cnt + 1)
}
useEffect(() => {
console.log('new state', cnt)
}, [cnt])
return (
<div>
<button onClick={updateState}>Change state</button>
</div>
)
}
I was able to trick React into calling setState synchronously by wrapping my code in setTimeout(() => {......this.setState({ ... });....}, 0);. Since setTimeout puts stuff at the end of the JavaScript event queue, I think React detects the setState is within it and knows it can't rely on a batched setState call (which would get added to the end of the queue).
In functional components I do this:
const handleUpdateCountry(newCountry) {
setIsFetching(() => true);
setCompanyLocation(() => newCountry);
setIsFetching(() => false);
}
Correct me if I'm wrong but as far as I know this is synchronous then and it also just worked in my situation.
Yes, there is a method with which we can make our synchronous setState. But its performance maybe not good as normally
For example, we have
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
data: 0
};
}
changeState(){
console.log('in change state',this.state.data);
this.state.data = 'new value here'
this.setState({});
console.log('in change state after state change',this.state.data);
}
render() {
return (
<div>
<p>{this.state.data}</p>
<a onClick={this.changeState}>Change state</a>
</div>
);
}
}
In this example, we change the state first and then render our component.
Short answer to your question is - NO, react doesn't have sync method setState.
In some cases, an alternative is using refs (createRef or useRef) instead of states.
//initial value of cnt:0
const cnt = React.createRef(0); //or React.useRef(0);
cnt.current++;
alert(cnt.current); //output: 1
So far the best solution for me is to use the callback function.
this.setState({cnt:this.state.cnt+1},() =>{
// other task which we want to run synchronously
alert(this.state.cnt);
})
according to the official documentation of react, we can make setState synchronous by passing the callback function in the second argument.

Categories

Resources