I think it might be silly question to ask but trust me I am beginner to reactJS . Could someone please explain me why we use prevState in ReactjS . I tried hard to understand but failed .
Here is my code. Please Help me to understand
state = {
placeName : '',
places : []
}
placeSubmitHanlder = () => {
if(this.state.placeName.trim()===''){
return;
}
this.setState(prevState => {
return {
places : prevState.places.concat(prevState.placeName)
};
});
};
prevState is a name that you have given to the argument passed to setState callback function. What it holds is the value of state before the setState was triggered by React; Since setState does batching, its sometimes important to know what the previous state was when you want to update the new state based on the previous state value.
So if multiple setState calls are updating the same state, batching setState calls may lead to incorrect state being set. Consider an example:
state = {
count: 0
}
updateCount = () => {
this.setState({ count: this.state.count + 1});
this.setState({ count: this.state.count + 1});
this.setState({ count: this.state.count + 1});
this.setState({ count: this.state.count + 1});
}
In the above code you might expect the value of count to be 4 but it would actually be 1 since the last call to setState will override any previous value during batching. A way to solve this to use functional setState:
updateCount = () => {
this.setState(prevstate => ({ count: prevstate.count + 1}));
this.setState(prevstate => ({ count: prevstate.count + 1}));
this.setState(prevstate => ({ count: prevstate.count + 1}));
this.setState(prevstate => ({ count: prevstate.count + 1}));
}
You use it when you want to override the current state with the last state's parameters.
From React docs :
According to the React docs "React may batch multiple setState() calls into a single update for performance. 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"
Link
Here is a demo with a commented-out code to give you more information: http://codepen.io/PiotrBerebecki/pen/rrGWjm
constructor() {
super();
this.state = {
value: 0
}
}
React docs: https://facebook.github.io/react/docs/reusable-components.html#es6-classes
The [React ES6 class] API is similar to React.createClass with the exception of getInitialState. Instead of providing a separate getInitialState method, you set up your own state property in the constructor.
Where does prevState come from?
The prevState comes from the setState api: https://facebook.github.io/react/docs/component-api.html#setstate
It's also possible to pass a function with the signature function(state, props). This can be useful in some cases when you want to enqueue an atomic update that consults the previous value of state+props before setting any values. For instance, suppose we wanted to increment a value in state:
this.setState(function(previousState, currentProps) {
return {
value: previousState.value + 1
};
});
Related
I want to use setState to update the object in state which called 'all_cart",
but not matter which method I tryed, it cannot trigger re-render, I know React will use === to check two object in state is changed or not.
Here is my code:
this.setState({
all_cart: {
...this.state.all_cart,
cart_data : _response['data'].data.cart_data
}
})
this.setState(({all_cart}) => ({
all_cart: {
...this.state.all_cart,
cart_data : _response['data'].data.cart_data
}
}))
How can I do?
According to the React docs if you have props, it is wrong approach:
// Wrong
this.setState({
counter: this.state.counter + this.props.increment,
});
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:
// Correct
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
And your code would look like this:
this.setState((state, props) => ({
all_cart: {
...state.all_cart,
cart_data : _response['data'].data.cart_data
}
}))
UPDATE:
However, if you do not use props, then it looks like your function is not is scope, so try to use arrow function. Let me show an example:
handleClick = () => {
this.setState(this.someApiResult())
}
So a complete stackblitz example can be seen here.
As per my understanding, you want to merge _response['data'].data.cart_data with the existing state which is all_cart,
in this case, you should try this
function updateCart(){
const updatedData = {...this.state.all_cart, ..._response['data'].data.cart_data};
this.setState({all_cart: updatedData});
}
I'm trying to build an app in React that will render interview questions from HTML, CSS, JS and React. I've set up a data.js file that is an array with objects inside that hold the Id, language, question and answer.
I've set up buttons for PreviousQuestion, NextQuestion and RandomQuestion.
I'm having trouble writing code for the PreviousQuestion, NextQuestion button. I've tried the following code on both but it doesn't work properly:
const questions = data.map((item) => item.question);
class App extends React.Component {
state = {
question: "",
count: 0,
};
prevQuestion = () => {
this.setState({
count: this.state.count - 1,
question: questions[this.state.count]
});
};
nextQuestion = () => {
this.setState({
count: this.state.count + 1,
question: questions[this.state.count]
});
I press nextQuestion this.state.count is 0 then 1 then 2 and then I press
PrevQuestion and this.state.count goes to 3 and only then back to 2 1 ...
Can anyone point me in the right direction on how to solve this so it always increments and decrements properly please?
The issue is that inside setState, your state has not changed yet. So in questions[this.state.count], this.state.count still reflects the old value, not the new one.
Therefore, this should fix your issue with prevQuestion:
prevQuestion = () => {
this.setState({
count: this.state.count - 1,
question: questions[this.state.count - 1]
});
};
And similarly for nextQuestion.
However, as Emile Bergeron stated in the comments, it would be better to only keep track of the count (i.e. the question index) in the state, and not the question itself. You can always access the relevant question using questions[this.state.count], saving you some headaches.
Use prevState in setState like the following one.
prevQuestion = () => {
this.setState(prevState => ({
count: prevState.count -1
question: questions[prevState.count -1]
}));
}
What's happening is that setState does not update the component state immediately, instead essentially queueing a state transition. See this answer. The answers / comments there also mention that you can use some component lifecycle methods such as componentDidUpdate to do something once a state transition has completed.
As others mentioned, you could update the question index by also adding / subtracting 1 from the state.count. Or, since I also don't think it's necessary to track the current question in the component's state, you could just refer to the question object in the render method:
const questions = data.map(item => item.question);
class Component extends Component {
constructor () {
super();
this.state = {
count: 0
}
}
nextQuestion () {
this.setState({ count: this.state.count + 1 });
}
previousQuestion () {
this.setState({ count: this.state.count - 1 });
}
render () {
// Get the question object based on the count in the component state
const question = questions[this.state.count];
return (
<!-- Stuff here -->
<p>{question}</p>
);
}
}
Case1.
handleRemovePlayer = id => {
this.setState(prevState => {
return {
players: prevState.players.filter(player => player.id !== id)
};
});
};
Case2.
// Arrow Func: become Component instance
incrementScore = () => {
this.setState(prevState => ({
score: this.state.score + 1
}));
};
decrementScore = () => {
if (this.state.score > 0) {
this.setState(prevState => ({
score: this.state.score - 1
}));
}
};
In setState(), why case1 cannot use this.players.filter instead of prevState.player? Both case1 and 2 use the same prevState callback.. Can anyone explain precisely regarding prevState?
Thanks in advance!
Currently, setState() is asynchronous inside event handlers.
Let assume that- you updated your state and you want to check state is updated or not.
for that you can use updater(callback) as 2nd argument to check updated state.
like this -
incrementScore = () => {
this.setState(prevState => ({
score: prevState.score + 1
}),()=>{
console.log(this.state.score)
});
};
Calls to setState are asynchronous - don’t rely on this.state to reflect the new value immediately after calling setState. Pass an updater function instead of an object if you need to compute values based on the current state ...for your reference setState in reactjs
SetState is an asynchronous method. So, if there are more than 1 setState execution methods, the second approach may result in the value which we are not interested in. But the first approach will make sure we always get the latest state value.
One should always use prevState instead of this.state.
I'm new to React, I was told by a book that it is incorrect to use setState method as the example below:
...
constructor(props) {
super(props);
this.state = {
counter: 0,
hasButtonBeenClicked: false
}
}
render() {
return (
<button onClick={ this.handleClick }>
Click
</button>
)
}
handleClick = () => {
this.setState({ counter: this.state.counter + 1 });
this.setState({ hasButtonBeenClicked: this.state.counter > 0 });
}
...
becuase React performs changes to state data asynchronously and may choose to group together several updates to improve performance.
I get the point, but since React performs changes asynchronously . there could be times and chances that the first setState method will get called first before the second setState method, it is just a matter of probability. But no matter how many times I tried, the second setState method always get called first, why?
Despite the fact that they're performed asynchronously, you're unlikely to see variance when testing from the same browser, and / or on the same hardware. Different browsers optimise instruction sets in slightly different ways, which is your best bet for examining performance (and why testing on all major desktop browsers is still recommended despite commercially Chrome often being "good enough").
The best way to do this - assuming you want them to fire in a specific order - is to chain the setState call as follows:
handleClick = () => {
this.setState({ counter: this.state.counter + 1 }, () => {
this.setState({ hasButtonBeenClicked: this.state.counter > 0 })
});
}
But I would recommend a different design regardless - tying the hasButtonBeenClicked logic to the counter is going to introduce problems down the line.
Don't reference this.state. Use callback variant of setState, get current state from there. That'll explicitly guard you against the "changed the state, but the state hasn't changed yet" situations.
And 2. Simply put those two updates into one run of setState:
this.setState((prevState) => ({
counter: prevState.counter + 1,
hasButtonBeenClicked: prevState.counter + 1 > 0 }));
If you have lots of states to update at once, group them all within the same setState:
Instead of:
this.setState({foo: "one"}, () => {
this.setState({bar: "two"});
});
Just do this:
this.setState({
foo: "one",
bar: "two"
});
Or in your case:
handleClick = () => {
this.setState({
counter: this.state.counter + 1,
hasButtonBeenClicked: this.state.counter > 0
});
}
You are right, React performs changes to state data asynchronously to improve performance.
In your case, you should write code like this:
handleClick = () => {
const counter = this.state.counter + 1;
this.setState({
counter: counter,
hasButtonBeenClicked: counter > 0
});
}
Or, you can use callback parameter of setState.
I see similar questions in this field but my question is different from these as i don't set state directly from this.state .
my code is
state = {
count: 0};
handleIncrement = product => {
console.log(product);
this.setState({ count: ++this.state.count });
};
as you see i don't set state directly but given me this error at this line
When you do this:
++this.state.count
This tries to mutate the state directly, because you're trying to do this:
this.state.count = this.state.count + 1
which changes the current value of state, but you should do this using setState not directly as the error says.
Do this instead:
this.setState({ count: this.state.count + 1 });
As a best practice, use a callback to avoid a race condition, such as:
this.setState(prev => {
return {
count: prev.count + 1,
};
});
You shouldn't mutate the state directly you can do as follows.
this.setState(prevState => ({
count: prevState.count+1;
}));
You can functionally update the state, therefore it takes the previous value of state, the props you are passing it and calculates the next state like so:
handleIncrement = product => {
console.log(product);
this.setState((state, props) => {
return {
count: state.count + 1
}
});
};