Why do i end up with an empty array in my state? - reactjs

I am setting my array's state in the componentDidMount but can't understand why it shows up as empty on the mount.
constructor(props){
super(props);
this.state = {
currentGroup: this.props.currentGroup,
eventHold: [],
idHold: []
}
}
componentDidMount(){
const groupRef = firebase.database().ref('groups').child(this.state.currentGroup).child('events');
var tempIdHold =[];
groupRef.on('value', snapshot => {
snapshot.forEach(function(snap) {
tempIdHold.push(snap.key)
})
this.setState({
idHold: tempIdHold
});
console.log(tempIdHold)
console.log(this.state.idHold)
})
this.loadGroupEvents(this.state.idHold);
}
The first console.log shows a populated tempId array but the second console.log right underneath it shows an empty state.id array. Why?

Because this.setState is a async function.
So, you can use the callback in setState function
this.setState({
idHold: tempIdHold
}, () => {
console.log(tempIdHold)
console.log(this.state.idHold)
});

this.setState is asynchronous which takes a callback that will invoke after the operation is finished try adding it and see the result
this.setState(
{ idHold: tempIdHold },
// our updated state will be available in our callback
() => console.log(this.state.idHold)
);

Related

setState is not toggling values and returning undefined React

This maybe a repetitive question but I am still not able to figure out why setState is not able to toggle boolean values? Here are the functions:
constructor(props){
super(props)
this.state = {
isPlaying: false
}
}
playButtonClicked = () => {
this.setState((prevState) => ({
isPlaying: !prevState.isPlaying
}))
console.log("updating state....state is="+this.isPlaying) // Its printing undefined
this.togglePlayPause();
}
Here's the div:
<button id="play-pause" onClick={this.playButtonClicked}></button>
Please let me know if you find the mistake. Thanks in advance.
this.setState((prevState) => ({
isPlaying: !prevState.isPlaying
}), function() {
console.log("updating state....state is="+this.state.isPlaying)
});
Setstate is async give console log in a callback.

React - Render happening before data is returned and not updating component

I can't get this to work correctly after several hours.
When creating a component that needs data from Firebase to display, the data is returning after all actions have taken place so my component isn't showing until pressing the button again which renders again and shows correctly.
Currently my function is finishing before setState, and setState is happening before the data returns.
I can get setState to happen when the data is returned by using the callback on setState but the component would have already rendered.
How do i get the component to render after the data has returned?
Or what would the correct approach be?
class CoffeeList extends Component {
constructor(props) {
super(props);
this.state = {
coffeeList: [],
}
}
componentDidMount() {
this.GetCoffeeList()
}
GetCoffeeList() {
var cups = []
coffeeCollection.get().then((querySnapshot) => {
querySnapshot.forEach(function (doc) {
cups.push({ name: doc.id})
});
console.log('Updating state')
console.log(cups)
})
this.setState({ coffeeList: cups })
console.log('End GetCoffeeList')
}
render() {
const coffeeCups = this.state.coffeeList;
console.log("Rendering component")
return (
<div className="coffee">
<p> This is the Coffee Component</p>
{coffeeCups.map((c) => {
return (
<CoffeeBox name={c.name} />
)
})}
</div >
)
}
}
Thanks
The problem is that you set the state before the promise is resolved. Change the code in the following way:
GetCoffeeList() {
coffeeCollection.get().then((querySnapshot) => {
const cups = []
querySnapshot.forEach(function (doc) {
cups.push({ name: doc.id})
});
console.log('Updating state')
console.log(cups)
this.setState({ coffeeList: cups })
console.log('End GetCoffeeList')
})
}

how to access state variable in componentDidMount in reactjs

"TypeError: Cannot read property 'state' of null"
the above error message is what i get....
following is my code
constructor(props) {
super(props)
this.state = {
desc: '',
}
}
componentDidMount = () => {
var ref = fire.database().ref("Employers/Employer1");
ref.orderByKey().on("child_added", function(snapshot) {
this.setState({
desc: snapshot.val()
})
console.log('====================================');
console.log(this.state.desc);
console.log(snapshot.val().Description);
console.log('====================================');
});
// snapshot.val() is the dictionary with all your keys/values from the '/store' path
}
This is because of this behavior in javascript. There are two ways to solve it. First to use arrow function, change third line to
ref.orderByKey().on("child_added", (snapshot) => {
other way is to assign value of this to another variable and use state by using that variable. forexample
const self = this;
ref.orderByKey().on("child_added", function(snapshot) {
this.setState({ desc: snapshot.val() })
console.log('====================================');
console.log(self.state.desc);
console.log(snapshot.val().Description);
console.log('====================================');
});
For understanding this you can read this article
componentDidMount is a life cycle hook, it need not be an arrow function.
change
componentDidMount = () => {
}
To
componentDidMount(){
}

In react, insert response array into setState

class Search extends React.Component {
constructor() {
super();
this.state = {
searchResult: {
"sr": []
}
}
this.handleChange = this.handleChange.bind(this);
this.onSubmit = this.onSubmit.bind(this);
this.setState = this.setState.bind(this)
}
onSubmit = formProps => {
console.log(formProps.searchItem);
cryptoSearch.searchNames(formProps.searchItem)
.then((names) =>
console.log(names),
this.setState({
sr: { names }
}),
console.log(this.state.sr)
) // [ 'BTC Lite', 'BTCMoon' ]
.catch(err => console.log(err))
};
handleChange(e) {
this.setState({ errorMessage: '' });
}
I want to use onsubmit function to display the search result from an api. I declared an array called sr in constructor, and onSubmit function, when I use a package(similar to axios) to get a names(response) array object, how can I insert the names array into sr array??
You can simply set in state while I am assuming names from the response is an array.
this.setState({
sr: names
},()=>{ console.log(this.state.sr);});
You can then verify in callback of setState as setState is an asynchronous type.
If names is an array and you want to update your sr property with this array you can use ES6 spread operator and set your state like that:
this.setState(prevState => ( {
searchResult: { ...prevState.searchResult, sr: names }
} )
);
If you set your state directly with sr like in your code you will loose other properties of searchResult and your state shape will change also something like this:
this.state = {
"sr": []
}
One other point is since setState is asynchronous if you console.log your state immediately after setting your state, you can't get healthy results. Instead use a callback for this:
this.setState(prevState => ( {
searchResult: { ...prevState.searchResult, sr: names }
} ), () => console.log(this.state.searchResult.sr)
);
or do not bother logging here with a callback and do it in your render method for logging purposes:
render(){
console.log(this.state.searchResult.sr);
return( .... )
}
If names is an array then you can set it to state as
onSubmit = formProps => {
console.log(formProps.searchItem);
cryptoSearch.searchNames(formProps.searchItem)
.then((names) =>
this.setState({
sr: names
}),
)
.catch(err => console.log(err))
};

setState in map function of react

My Requirement is to update the state value in map function of componentWillReceiveProps.
In console log all I am getting is 1s but sub.subscribed contain 0s and 1s
Reference of console window: http://prntscr.com/jqifiz
constructor(props) {
super(props);
this.state = {
regionAll: [],
};
}
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
this.setState({
regionAll: [
...this.state.regionAll,
sub.subscribed
]
},()=>{
console.log(this.state.regionAll,'sub');
})
})
}
Is this a correct way to update state in reactjs?
setState is async.In Array#map, it called multiple time.Only last value is added in array regionAll and not all because of async setState call with multiple value.
You can collect all sub.subscribed value in single array with Array#reducer then perform state update.
if (nextProps.apiData !== false) {
let sub = nextProps
.apiData
.data
.datacenter
.category
.reduce((accum, sub) => [
...accum,
sub.subscribed
], [])
this.setState({
regionAll: [...sub]
}, () => {
console.log(this.state.regionAll, 'sub');
})
}
The problem arises because setState calls are batched and you are updated React state based on prevState, you should instead use functional state for such cases
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
this.setState(prevState => ({
regionAll: [
...prevState.regionAll,
sub.subscribed
]
}),()=>{
console.log(this.state.regionAll,'sub');
})
})
}
However its a bad idea to call setState in a map, you can instead get the data from map and call setState just once like
componentWillReceiveProps(nextProps){
if(nextProps.apiData !== false ){
const subscribed = nextProps.apiData.data.datacenter.category.map((sub)=> {
console.log(sub.subscribed,'sub.subscribed');
return sub.subscribed;
})
this.setState(prevState => ({
regionAll: [
...this.state.regionAll,
...subscribed
]
}),()=>{
console.log(this.state.regionAll,'sub');
})
}

Resources