React: How to properly set state at first load - javascript

I have this function that gets some initial data in db:
private getFilter() {
let getResp;
let defaultselected = [];
axios.get(getEndPoint)
.then(response => {
getResp = response.data;
defaultselected = JSON.parse(getResp['JsonValue']);
this.setState({ customFilter: defaultselected});
})
}
And this function that loads data from db also:
getOpenClaims = (pageNumber: number) => {
console.log('getOpenClaims', this.state.customFilter)
//logic here
}
on first load, this.state.customFilter is undefined but if I getOpenClaims is executed again by other way, this.state.customFilter has already it's expected value and I already validated that getFilter has a return data from console.log
And here is how I called both functions:
async componentDidMount() {
document.title = "Claim Aging";
this.getOpenClaims(1);
}
componentWillMount() {
this.getFilter();
}
What Am I missing here?

You need to wait for execution of getFilter()before calling getOpenClaims
A little refactoring can help
private async getFilter() { // made this function a promise, using async/await
let getResp;
let defaultselected = [];
const response = await axios.get(getEndPoint)
getResp = response.data;
defaultselected = JSON.parse(getResp['JsonValue']);
this.setState({ customFilter: defaultselected});
}
async componentDidMount() {
document.title = "Claim Aging";
await this.getFilter(); // wait for promise to resolve and then call `getOpenClaims`
this.getOpenClaims(1);
}

constructor(props) {
super(props);
this.state = {
customFilter: value
}
}
You can define the state in the constructor.

Related

Async function call repeated inside of class component

I've created an Asynchronous function to call a weather API that will return the requested information submitted. The function works fine however when I call this function inside of a Class Component the result is returned twice. This isn't exactly a breaking bug but I'd rather not have two API calls occurring if not necessary and I'm curious as to why this method is being called twice in the first place.
Here is my code.
async function submitQuery(props) {
//Incase I decide to add aditional parameters such as city, country etc.. I've decided to place the zip property in an object so I can just
//add additional properties in the submissions object
const submission = {
zip: props,
};
if (!Number(props)) return console.log("this is not a number");
const { error } = await validate(submission);
if (error) return console.log(error.message);
const config = {
method: "get",
url: `https://api.openweathermap.org/data/2.5/weather?zip=${props}&appid=${apiKey}`,
headers: {},
};
/*
const query = await axios(config).then(function (response) {
const result = response.data.main;
return result;
});
*/
//console.log(query);
return 4;
}
class WeatherReport extends React.Component {
getResults = async () => {
const result = await submitQuery("08060");
console.log(result);
};
render() {
return (
<div className="reportContainer">
<WeatherCard getResults={this.getResults} />
</div>
);
}
}
const WeatherCard = ({ getResults }) => {
getResults();
return <div></div>;
};
The problem is that you're calling getResults in the render method of WeatherCard, move it to a useEffect so its not called every time
const WeatherCard = ({ getResults }) => {
React.useEffect(() => {
getResults();
}, [getResults]);
return <div></div>;
};

setState while looping through an array of props - React ComponentDidUpdate

I am working on a project and needed to setState after componentDidMount.(The props am expecting in the child component are derived at mount. Hence i can only setState after)
Only option i was able to come up with was, using componentDidUpdate.
The props parent component is an array derived from an axios fetched data.
The goal here is to loop though the array and fetch data for each from the URL showing in the code below to then setState of the child component.
Trying what i normally do, I could not stop the infinite loop fired at componentDidUpdate.
Here is my code.
Parent
render(){
return (
<div className="App">
<EachCountry countryList= {this.state.CountriesList}/>
</div>
Child component
async componentDidUpdate(prevProps, prevState, snapshot){
if(this.state.countryList.length < this.props.countryList.length){
await this.props.countryList.map(m=>{
axios ({
method:"get",
url:`/countryupdate/${m}`
}).then(res=>{
console.log(res.data)
this.setState(crntState=>({
countryList:[...crntState.countryList, res.data]
}))
})
})
}
}
console log works perfectly fine. But when i tried to setState, i run into infinite loop with something like 5000 plus error messages.
And my other trick was
async componentDidUpdate(prevProps, prevState, snapshot){
if(this.state.countryList.length < this.props.countryList.length){
await this.props.countryList.map(m=>{
var newdata = axios ({
method:"get",
url:`/countryupdate/${m}`
})
console.log(newdata)
this.setState(crntState=>({
countryList:[...crntState.countryList, newdata.data]
}))
})
}
}
And this one returns promises and not the needed data.
Help Fam
What am i missing?
Your issue is likely caused by derived state: state that is dependent on props and is an anti pattern in react:
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#when-to-use-derived-state
See below on a plausible workaround, though its recommended you restructure your data flow.
Try something like this to first only send 1 update to state:
async componentDidMount(){
//variable to store new data
const allNewData = [];
//an async data fetcher
const getNewData = async(m) => {
let newData = await axios({
method: "get",
url: `/countryupdate/${m}`
})
allNewData.push(newData.data);
}
//an async for loop
async function updateData() {
for (const m of countryList) {
await getNewData(m);
}
this.setState(crntState => ({
countryList: [...crntState.countryList, ...allNewData]
}))
}
await updateData();
}
If the above doesn't work (which it might not), then use getDerivedStateFromProps instead of componentDidMount and replace setState with a return obj
static getDerivedStateFromProps(props, state) {
if (this.state.countryList.length < this.props.countryList.length) {
...
return {
countryList: [...state.countryList, ...allNewData]
};
}
let newState = await updateData();
return newState;
}
If that doesn't work, then revert back to componentDidMount and use shouldComponentUpdate for the conditional
shouldComponentUpdate(nextProps, nextState) {
return this.state.countryList.length != nextState.countryList.length;
}
Incase I didn't get the syntax just right look at this code snippet
function mockAxios(m) {
return new Promise(function(resolve, reject) {
setTimeout(() => resolve({
data: `${m}'s data`
}), 1000)
});
}
function setState(arr) {
console.log(arr);
console.log("state has been set")
}
const countryList = ["A", "B", "C", "D", "E"];
///////////////////////////////////////////////
async function componentDidMount() {
const allNewData = [];
async function getNewData(m) {
let newData = await mockAxios(m);
allNewData.push(newData.data);
}
async function updateData() {
for (const m of countryList) {
await getNewData(m);
console.log("fetching data...")
}
setState(allNewData);
}
await updateData();
}
componentDidMount();

Having troubles getting returned value from an async/await function on ES6, how to solve it?

I am attempting to subtract an id from an array to use it a parameter but I am having some issues on the try.
This is the code:
const takeId = async (): Promise<any> => {
const newArr = await departmentResolution[0].template;
getTemplateId = await newArr.map((item: any) => {
return item;
});
return getTemplateId[0]; // this returns { id: 11, anotherKey: 'value' }
}
const setId = async () => {
const el = await takeId();
return el.id;
}
If console.log(setId()) I get Promise and all I need is the value returned by it to use here:
<Container>
<Breadcrumb />
{handleTemplate(setId())}
</Container>
Don't use the async method in your render template.
In the template, use a state variable which you update when the promise resolves. When a state value is used, once the state is updated, it triggers as a re-render
state = {
someKey: 'someInitialValue'
};
async myMethod() {
const myAsyncValue = await anotherAsyncMethod();
this.setState({ someKey: myAsyncValue });
}
render() {
return <div>{this.state.someKey}</div>;
}

AsyncStorage.getItem returns undefined(even with .then and .parse)

I'm attempting to store data in AsyncStorage and load them back(obviously). The .setItem function works, and the notification pops up at the bottom of the iOS simulator when I call it. However, the .getItem function doesn't work, and when I console.log it, returns undefined. I have two functions to store and fetch the data:
setData = (rawDataToStore, keyToStore) => {
data_store = JSON.stringify(rawDataToStore);
AsyncStorage.setItem(keyToStore, data_store, () => {
console.warn('Stored data!')
} )
}
getData = (keyToSearch) => {
AsyncStorage.getItem(keyToSearch).then(storage => {
parsed_data = JSON.parse(storage);
return parsed_data
}).catch(e => console.warn(e))
}
I just tested the functions in my render():
to save the data:
this.setData({value: 1}, "test_data");
to load the data:
console.log(this.getData("test_data"));
The console.log just returns undefined.
I'm completely new to Asyncstorage, but what am I doing wrong?
Your console.log returns undefined ... cause, your setData function hasn't finished its job yet ... you have to await for it first, cause it's an async operation.
class YourComponent extends React.Component {
async componentDidMount() {
await this.setData();
const data = await this.getData();
console.log('data returned', data);
}
setData = async (rawDataToStore, keyToStore) => {
data_store = JSON.stringify(rawDataToStore);
await AsyncStorage.setItem(keyToStore, data_store);
};
getData = async keyToSearch => {
let parsed_data = null;
const storage = await AsyncStorage.getItem(keyToSearch);
if (storage) {
parsed_data = JSON.parse(storage);
console.log('Data in AsyncStorage: ', parsed_data);
}
return parsed_data;
};
}

How do I update Reactjs State with data I retrieved using a fetch API call?

I made a fetch API call in react.js and put it inside a variable defined within the function containing the fetch function. But how do I transfer this value to one of the variables in the state? I can get to the point where I console.log the variable, but still I'm not able to figure out how to update one of the state variables so that I can then display the retrieved data onto the page.
import React from 'react';
class Stock extends React.Component {
constructor(props) {
super(props);
this.state = {
stockInfo: '100'
}
}
componentDidMount() {
this.fetchStock();
}
fetchStock() {
const API_KEY = 'api key goes here';
let TimeInterval = '60min';
let StockSymbol = 'AMZN';
let API_Call = `https://www.alphavantage.co/query?function=TIME_SERIES_INTRADAY&symbol=${StockSymbol}&interval=${TimeInterval}&outputsize=compact&apikey=${API_KEY}`;
let stockHistoryDatabase = {};
let stockHistoryDatabaseString;
fetch(API_Call)
.then(
function(response) {
return response.json();
}
)
.then(
function(data) {
console.log(data);
for (var key in data['Time Series (60min)']) {
// push the key value pair of the time stamp key and the opening value key paired together into an object with a key value pair data set storage.
var epochKeyTime = new Date(key);
epochKeyTime = epochKeyTime.getTime();
stockHistoryDatabase[epochKeyTime] = data['Time Series (60min)'][key]['1. open'];
}
console.log(stockHistoryDatabase);
stockHistoryDatabaseString = JSON.stringify(stockHistoryDatabase);
console.log(stockHistoryDatabaseString);
}
);
}
handleChange = () => {
this.setState({
stockInfo: 'hello'
});
}
render() {
return(
<div>
<h1>Stocks</h1>
<p>{this.state.stockInfo}</p>
<button onClick={this.handleChange}>Change</button>
</div>
);
}
}
export default Stock;
this is my code in entirety. I know how to change the state using a separate function that is called from a button click on the same page, but I'm unable to get the data stored in the variable 'stockHistoryDatabaseString' to replace the state 'stockInfo'.
Thank you for the help!
I had a similar problem. My solution to this problem was to store this context of react class into one variable and then use it in any scope below it.
fetchStock() {
const pointerToThis = this; // points to context of current react class
fetch(API_Call)
.then(function(response) {
return response.json();
})
.then(function(data) {
console.log(pointerToThis); // you can use pointerToThis which in turn points to react class
});
}
First inside constructor add
this.fetchStock = this.fetchStock.bind(this);
Update fetchStock function like this:
fetchStock() {
const API_KEY = 'api key goes here';
let TimeInterval = '60min';
let StockSymbol = 'AMZN';
let API_Call = `https://www.alphavantage.co/queryfunction=TIME_SERIES_INTRADAY&symbol=${StockSymbol}&interval=${TimeInterval}&outputsize=compact&apikey=${API_KEY}`;
let stockHistoryDatabase = {};
let stockHistoryDatabaseString;
fetch(API_Call)
.then(response => response.json())
.then(data => {
for (var key in data['Time Series (60min)']) {
var epochKeyTime = new Date(key);
epochKeyTime = epochKeyTime.getTime();
stockHistoryDatabase[epochKeyTime] = data['Time Series (60min)'][key]['1. open'];
}
this.setState({stockInfo: stockHistoryDatabase})
//Set your state here.
stockHistoryDatabaseString = JSON.stringify(stockHistoryDatabase);
}
);
}
Since you are calling fetchStock after component is mounting. You can use arrow function as follows.
.then((data) => {
// use data here
this.setState({ ... }) // set you state
})
or if you are not comfortable using arrow function, then I believe you can create a function to handle the promise e.g. handleData
.then(this.handleData)
in class
// pseudo code
class YourClass extends React.Component {
componentDidMount() {
this.fetchStock()
}
handleData = (data) => {
// process your data and set state
}
fetchStock() {
// your API call
fetch(API_CALL).then(this.handleData);
}
render() {}
}
If you are invoking fetchStock on user operation, such as button click, then you can provide appropriate context to fetchStock by binding it to React class you've created as follows:
constructor() {
this.fetchStock = this.fetchStock.bind(this);
}
or there is another way to achieve the same (perhaps cleaner way):
fetchStock = () => {
}

Categories