In react, insert response array into setState - reactjs

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))
};

Related

setState not returned from render when using Axios

I'm using axios to get data from an endpoint. I'm trying to store this data inside the state of my React component, but I keep getting this error:
Error: Results(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
I've struggled with many approaches: arrow functions etc., but without luck.
export default class Map extends Component {
constructor() {
super();
this.state = {
fillColor: {},
selectedCounty: "",
dbResponse: null,
};
}
getCounty(e) {
axios.get("/getWeatherData?county=" + e.target.id)
.then((response) => {
this.setState(prevState => {
let fillColor = {...prevState.fillColor};
fillColor[prevState.selectedCounty] = '#81AC8B';
fillColor[e.target.id] = '#425957';
const selectedCounty = e.target.id;
const dbResponse = response.data;
return { dbResponse, selectedCounty, fillColor };
})
}).catch((error) => {
console.log('Could not connect to the backend');
console.log(error)
});
}
render() {
return (
<div id="map">
<svg>big svg file</svg>
{this.state.selectedCounty ? <Results/> : null}
</div>
)
}
I need to set the state using prevState in order to update the fillColor dictionary.
Should this be expected? Is there a workaround?

What is the better way to update other state variable which is dependent on another state variable while re-rendering?

The scenario is, after mounting the component, in event listener I am setting a state variable and other state variables are being set by making a rest call from backend.
so far what I did is I am using componentWillUpdate and making rest call and setting all the required states.
I tried using componentWillUpdate method to calculate and set other state variables. But its re-rendering multiple times. I guess I am definitely doing something wrong here.
export default class Person extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
name: this.props.name,
age: "",
country: ""
};
}
componentDidMount() {
this.setDerivedStates();
}
componentWillUpdate() {
this.setDerivedStates();
}
attachListner() {
document.addEventListner("customEvent", () => {
this.setState({ name: something });
});
}
setDerivedStates() {
FetchService.get("url1" + this.state.name).then(response =>
this.setState({ age: response.age})
);
FetchService.get("url2" + this.state.name).then(response =>
this.setState({ country: response.country })
);
}
render() {
return (
<div>
<p>{this.state.name}</p>
<p>{this.state.age}</p>
<p>{this.state.country}</p>
</div>
);
}
}
I want to re-render the component once with all the new state variables.
Please suggest how should I do it. which lifecycle method and how should I use to set all these states?
You can use Promise.all to batch the two fetches so you only have call this.setState once -
const [resp1, resp2] = await Promise.all([
FetchService.get("url1" + this.state.name);
FetchService.get("url2" + this.state.name);
]);
this.setState({ age: resp1.age, country: resp2.country });
Also, componentWillUpdate is considered unsafe and will be deprecated in the future. I would suggest using componentDidUpdate instead -
componentDidUpdate = (prevProps, prevState) => {
if (prevState.name !== this.state.name) {
this.setDerviedStates();
}
}
You can wrap both fetches in Promise.all which will wait for both Promises to resolve, if one fails you will have no access to any Promise/s that resolves successfully and the operation will throw an error.
Add componentDidUpdate to check if the name state has changed, if so refetch.
componentDidUpdate(prevProps, prevState) {
if (prevState.name !== this.state.name) {
this.setDerivedStates();
}
}
async setDerivedStates() {
const url = `url${this.state.name}`;
try {
const [age, country] = await Promise.all([
FetchService.getAge(url),
FetchService.getCountry(url),
]);
this.setState({ age, country });
} catch (e) {
console.log('something went wrong', e);
}
}

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(){
}

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

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)
);

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