this.state = { isOpen: false };
onClickHandler() {
this.setState(currentState => ({
isOpen: !currentState.isOpen
}));
}
There is only one state here,isOpne,but when i set a state ,there i see currentState, what does this means
To calculate new state using the current state, you can very well do:
this.state = { isOpen: false };
onClickHandler() {
this.setState(currentState => ({
isOpen: !this.state.isOpen
}));
}
However this is bad practice as mentioned in docs:
State Updates May Be Asynchronous. 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.
Thus when we need to calculate next state from the current state, the setState provides access to the current state as setState(myCurrentState => {...});. You can even access props as the second parameter like setState((myCurrentState, myProps) => {...});.
So in your case currentState simply refers to your component state at the given moment just before the state update that's going to happen.
what does this means?
If you go to the docs there you can find that there are two ways to update the state.
Via using object way
Via passing a function.
1.
With this you need to pass just the property of the state object to update. like:
this.setState({isOpen: !this.state.isOpen});
2.
In this case you are naming it as currentState it refers to the previous state, which is not yet updated.
this.setState(currentState => ({ // <---currentState is named as prevState in docs
isOpen: !currentState.isOpen
}));
You can think of it as this.state.
in above example currentState is previous state which you have used before when you have multiple items like many users list and want to compare and want to neglate some of them with true false then you can use but for toggle state you can simply use below code:
this.state = { isOpen: false };
onClickHandler() {
this.setState({
isOpen: !this.state.isOpen
});
}
Related
I'm comparing props from within componentDidUpdate and trying to update the state and, after that, fetch some data (which depends of some state params).
componentDidUpdate(prevProps) {
if (prevProps.location.search.split("?description=")[1] !==
this.props.location.search.split("?description=")[1]) {
this.setState({
searchParams: this.getInitialSearchParams(),
isLoaded: false,
entities: []
})
this.fetchMore()
}
}
But, as the question suggests, when I'm going to fetch the data, the function is using a previous state.
Sandbox: https://codesandbox.io/s/0y8cm
To evaluate the error do the following: use the navbar to search one of the entities. Check the console to see the state. Do another search in the navbar to the same entity. Check the console again and see the unchanged state.
What am I doing wrong?
Thanks a lot!
This is because setState is asynchronous and the value is not set immediately after the call. You should use its callback to follow up.
setState(updater, [callback]);
This is useful for when you want to use the state values right after updating them. So instead you would have:
componentDidUpdate(prevProps) {
if (prevProps.location.search.split("?description=")[1] !==
this.props.location.search.split("?description=")[1]) {
this.setState({
searchParams: this.getInitialSearchParams(),
isLoaded: false,
entities: []
}, this.fetchMore); //or () => this.fetchMore() if you want to send params
}
}
You can read more about setState here: https://reactjs.org/docs/react-component.html#setstate
It's very useful to read about the setState and React's lifecycle as well.
Hope this was helpful.
What is the difference between the two syntax?
setValues(values => ({
...values, [event.target.name]: event.target.value
}))
setValues({
[event.target.name]: event.target.value
})
Based upon the name setValues I assume you are referring to functional component state. (useState hook updates don't work quite the same as class-based component's setState lifecycle function)
Using the spread syntax allows for maintaining existing state, i.e. the new update [event.target.name]: event.target.value is merged into current state.
Given state { 'foo': 'bar' }
setValues(values => ({
...values, ['bizz']: 'buzz'
}))
New state { 'foo': 'bar', 'bizz': 'buzz' }
Without spreading in the previous state you are simply overwriting it with just an object {[event.target.name]: event.target.value}, so all previous state is lost.
Given state { 'foo': 'bar' }
setValues({
['bizz']: 'buzz'
})
New state { 'bizz': 'buzz' }
There are actually a couple things going on here. First is the spread syntax, the other is what is called a functional update. Functional updates allow the update to access the current state and make changes. This is a necessity when the next state depends on the previous state, like incrementing counters, and multiple state updates can be queued up during each render cycle.
setCount(count => count +1)
In the case of a form component where each property is an independent piece of state, then the following syntax is ok since each update to a field overwrites the current value:
setValues({
...values,
[fieldName]: fieldValue
})
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.
For example, this code may fail to update the counter:
// 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
}));
We used an arrow function above, but it also works with regular functions:
// Correct
this.setState(function(state, props) {
return {
counter: state.counter + props.increment
};
});
Read more here on their official documentation
the 'values' parameter contains the current values held in state , so by using the spread operator you preserve the values held in the current state and add in new ones.
In the second example you are just setting the state to the event.target.name and overwriting the previously held state.
For example , suppose the app had called setValues three times with three events : event1 , event2 and event3
In the first case you wrote about your state would be { event1, event2, event3 }. in the second case your state would be { event3 }
I have refereed this , this and this link to find solution but none of them work for me.
I have decleared state with array type and other state with null.
this.state = { from: '', to: '',journeyDate: '',searchDetails: [] }
As user fill up search form with source city , destination city and journey date I set these state like below .
let from = this.state.from;
let to = this.state.to;
let journeyDate = moment(this.state.journeyDate).format('YYYY-MM-DD');
final step to push these all variables into searchDetails array state. for that I have tried below options but none of them worked.
OPTION 1
this.setState({ searchDetails: [...this.state.searchDetails, from] });
OPTION 2
this.setState(prevState => ({
searchDetails: [...prevState.searchDetails, from]
}))
OPTION 3
let newState = this.state.searchDetails.slice();
newState.push(from);
this.setState({
searchDetails: newState
});
console.log('final state is : ' + this.state.searchDetails);
every time console log remains empty.
any advice ?
Actually setState is an asynchronous method. That means right after writing setState, you cannot expect the state to be changed immediately. So console.log right after setState may show you last state instead of new state. If you want to log the updated state then use the callback of setState which is called when state is updated like this
this.setState({ searchDetails: [...this.state.searchDetails, from] }, () => {
console.log(this.state.searchDetails)
});
Try :
this.setState({
searchDetails: newState // use any of your 3 methods to set state
},() => {
console.log('final state is : ' + this.state.searchDetails);
});
setState() does not always immediately update the component. It may
batch or defer the update until later.
This makes reading this.state
right after calling setState() a potential pitfall.
Instead, use
componentDidUpdate or a setState callback (setState(updater,
callback)), either of which are guaranteed to fire after the update
has been applied. If you need to set the state based on the previous
state, read about the updater argument below.
For More Detail, Please read : https://reactjs.org/docs/react-component.html#setstate
I am learning to use Firebase using reactJS. I am trying to update my firebaseList state to match the Firebase database.
...
const dbRef = firebase.initializeApp(config).database().ref().child('text');
class App extends Component {
constructor(){
super();
this.state = {
text: "",
firebaseList: {}
}
}
componentDidMount(){
dbRef.on('value', snap => {
console.log(snap.val());
this.setState({
firebaseList: snap.val()
});
console.log('firebaseList: ', this.state.firebaseList);
});
}
...
When I go to chrome console after pushing a new string, "This is a test!", this is displayed:
Object {-KeoiS8luCsuKhzc_Eut: "asdf", -Keol-2Si05dmkmuac8l: "This is a test!"}
firebaseList: Object {-KeoiS8luCsuKhzc_Eut: "asdf"}
Why is my firebaseList state behind by one element? Why does snap.val() have two key-value pairs and firebaseList only has one key-value pairs?
this.setState is not guaranteed to be synchronous, because they can be processed in batches. This means that although you call console.log in your code after your setState, the state may not have actually changed yet.
From the React 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.
If you would like to check if your state is updated, you can either used a callback as the second argument to setState or put some logic in the shouldComponentUpdate(nextProps, nextState) lifecycle method.
Example:
componentDidMount(){
dbRef.on('value', snap => {
console.log(snap.val());
this.setState({
firebaseList: snap.val()
}, () => console.log('firebaseList: ', this.state.firebaseList))
});
}
or
shouldComponentUpdate(nextProps, nextState) {
if (this.state.firebaseList !== nextState.firebaseList) {
console.log('firebaseList: ', nextState.firebaseList);
}
}
setState Documentation: (Note the function signature, setState(nextState, callback))
https://facebook.github.io/react/docs/react-component.html#setstate
shouldComponentUpdate Documentation:
https://facebook.github.io/react/docs/react-component.html#shouldcomponentupdate
In React the this.setState method sets state. For instance below I set the state for name to Thomas.
this.setState({'name': 'Thomas'})
I need another object. A state property that comprises of other state properties. For instance below.
let name = 'Thomas'
this.setState({name})
console.log(this.state.data) // => undefined
let age = '26'
let data = {age}
this.setState(data)
console.log(this.state.data) {data: {age: '26'}}
this.stateChanged(state => {
let data = state.data || {}
if (state.name) data.name = state.name
this.setState({data})
})
this.setState({name})
console.log(this.state.data) // => {data: {age: '26', name: 'Thomas'}}
Is there any equivalent this.stateChanged method in React?
React gives us a couple explicit options to determine when the state's changed, or component has been updated. The third option being more of an implicit approach for your particular case:
Use the callback provided in setState(function|object nextState, [function callback]):
onClick(e) {
this.setState({
foobar: e.target.value
}, this.doAfter)
}
doAfter() {
console.log(`Just set FOOBAR to ${this.state.foobar}`)
}
Per the documentation, we can rely on this method to be invoked after the state was asynchronously set.
Use the componentDidUpdate lifecycle method:
componentDidUpdate() {
console.log(`Component updated: Foobar is ${this.state.foobar}`)
}
Per the documentation, this function will be invoked immediately after the component has rendered itself, thereby guaranteeing we have access to the latest state changes.
Compile all necessary state information in the render() method.
render() {
const compilation = {foobar: `${this.state.foobar}-bar`}
return (
<div>...</div>
)
}
Basically, just compose the object how you'd like it before returning a JSX object from render(), however the first two options above might suffice better for your needs.
Example
http://codepen.io/mikechabot/pen/KzyVVw?editors=0011