Store JSON in a variable in a REACT Helper component - javascript

I'm new in react and i'm trying to do a fetch and then store the data in a variable.
I don't understand why my SetMovieResponse is not working, I tried to stringify the JSON I'm sending but each time I look at my console the var is undefined.
The response is fully clear but when I assign and display the data, it's undefined
Here is my code :
import React, { setState } from 'react';
const API_KEY = process.env.REACT_APP_API_KEY;
const PERSON_KEY = process.env.REACT_APP_PERSON;
const MOVIE_KEY = process.env.REACT_APP_MOVIE;
class Helper extends React.Component {
constructor() {
super()
this.state = {
answer: "",
actorAnswer: [],
movieAnswer: {},
myInit: {method: "GET", mode: "cors"}
}
}
setActorResponse = (response) => {
this.setState({ actorAnswer: response});
}
setMovieResponse = (response) => {
console.log("get call ? ",response)
this.setState({ movieAnswer: JSON.stringify(response)});
console.log("get SET ? ",this.movieAnswer)
}
fetchPersonFunction = (randomActor) => {
fetch(`${PERSON_KEY}${randomActor}?api_key=${API_KEY}`, this.myInit)
.then(res => {
return res.json();
})
.then(response => {this.setActorResponse(response.total)})
return (this.actorAnswer)
}
fetchMovieFunction = (randomMovie) => {
fetch(`${MOVIE_KEY}${randomMovie}?api_key=${API_KEY}`, this.myInit)
.then(res => {
return res.json();
})
.then(response => {this.setMovieResponse(response)
console.log("good response = ", response)
})
console.log("END res = ", this.movieAnswer);
return (this.movieAnswer)
}
}
export default Helper
So the console.log("get call ? ",response) Always display the JSON but the console.log("get SET ? ",this.movieAnswer)
I'm trying to call those function in a Game.js file like that :
const Help = new Helper();
console.log("MOVIE res = ",Help.fetchMovieFunction(randomMovie));
Who is just under and suppose to set movieAnswer is always undefined.
Is there any reason why my movieAnswer stay undefined when using setState ?
Thank's for you're help

You are accessing the state wrong. Your component state is accessible inside this.state property. ie, The value movieAnswer is available inside the this.state property.
Simply change all your state items to below,
console.log("get SET ? ",this.state.movieAnswer)
also change this.actorAnswer to,
this.state.actorAnswer
Also I don't think you need those return statements at the end of fetchPersonFunction and fetchMovieFunction functions.

Related

React: How to properly set state at first load

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.

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

How to get the data from promise when i cant get forEach is not a function?

i have an a data using fetch on some API and looks like this.
and i'm fetching this data (as you can see), i'm using a promise here is the code
data-source
const baseUrl = "https://api.musixmatch.com/ws/1.1";
const apiKey = "78fa4727ab9c4495d4fc07dae75f775b";
const chartTrackPopular = "chart.tracks.get?
chart_name=top&page=1&page_size=5&country=jp&f_has_lyrics=1";
const chartTrackAll = "chart.tracks.get?chart_name=top&page=1&page_size=5&country=us&f_has_lyrics=1";
class DataSource {
// initialize the data
static getPopularLirik(){
return fetch(`${baseUrl}/${chartTrackPopular}&apikey=${apiKey}`)
.then(response => {
return response.json();
})
.then(responseJson => {
if(responseJson.message.body.track_list) {
return Promise.resolve(responseJson.message.body.track_list);
} else {
return Promise.reject(`error`)
}
})
.catch(error => {
console.log(error);
})
}
}
export default DataSource;
i get the data but when i try to loop it, i cant get the data. Here is how i want to get the data
popular-list.js
import './popular-item.js'
class PopularList extends HTMLElement {
constructor(){
super();
this.shadowDOM = this.attachShadow({mode: "open"});
}
set items(item){
this._item = item;
this.render();
}
render(){
this.shadowDOM.innerHTML = "";
this._item.forEach(musicitem => {
const itemElements = document.createElement("popular-item");
itemElements.items = musicitem;
// this.shadowDOM.appendChild(itemElements);
})
// let result = DataSource.getPopularLirik();
// console.log(this._item.track);
}
};
customElements.define("popular-list", PopularList);
and this is the error i got
VM55555 popular-list.js:20 Uncaught TypeError: this._item.forEach is not a function
at HTMLElement.render (VM55555 popular-list.js:20)
at HTMLElement.set items [as items] (VM55555 popular-list.js:15)
at renderResult (VM55554 main.js:13)
at HTMLDocument.main (VM55554 main.js:19
i don't know how and why this can be happened, i thought if i try to loop the data i can get the value but i dont even cant to forEach it. How do i resolve this?

Perform two functions simultaneously

I have a function that refreshes the data of my component when the function is called. At this moment it only works for one component at a time. But I want to refresh two components at once. This is my refresh function:
fetchDataByName = name => {
const { retrievedData } = this.state;
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
retrievedData[name] = data;
this._isMounted && this.setState({ retrievedData });
});
};
My function is called like this:
refresh("meetingTypes");
As it it passed as props to my component:
return (
<Component
{...retrievedData}
{...componentProps}
refresh={this.fetchDataByName}
/>
);
I tried passing multiple component names as an array like this:
const args = ['meetingTypes', 'exampleMeetingTypes'];
refresh(args);
And then check in my fetchDataByName function if name is an array and loop through the array to fetch the data. But then the function is still executed after each other instead of at the same time. So my question is:
What would be the best way to implement this that it seems like the
function is executed at once instead of first refreshing meetingTypes
and then exampleMeetingTypes?
Should I use async/await or are there better options?
The fetchData function:
fetchData = (fetch, callback) => {
const { componentProps } = this.props;
let { route, params = [] } = fetch;
let fetchData = true;
// if fetcher url contains params and the param can be found
// in the component props, they should be replaced.
_.each(params, param => {
if (componentProps[param]) {
route = route.replace(`:${param}`, componentProps[param]);
} else {
fetchData = false; // don't fetch data for this entry as the params are not given
}
});
if (fetchData) {
axios
.get(route)
.then(({ data }) => {
if (this.isMounted) {
callback(null, data);
}
})
.catch(error => {
if (error.response.status == 403) {
this._isMounted && this.setState({ errorCode: 403 });
setMessage({
text: "Unauthorized",
type: "error"
});
}
if (error.response.status == 401) {
this._isMounted && this.setState({ errorCode: 401 });
window.location.href = "/login";
}
if (error.response.status != 403) {
console.error("Your backend is failing.", error);
}
callback(error, null);
});
} else {
callback(null, null);
}
};
I assume fetchData works asynchronously (ajax or similar). To refresh two aspects of the data in parallel, simply make two calls instead of one:
refresh("meetingTypes");
refresh("exampleMeetingTypes");
The two ajax calls or whatever will run in parallel, each updating the component when it finishes. But: See the "Side Note" below, there's a problem with fetchDataByName.
If you want to avoid updating the component twice, you'll have to update fetchDataByName to either accept multiple names or to return a promise of the result (or similar) rather than updating the component directly, so the caller can do multiple calls and wait for both results before doing the update.
Side note: This aspect of fetchDataByName looks suspect:
fetchDataByName = name => {
const { retrievedData } = this.state; // <=============================
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
retrievedData[name] = data; // <=============================
this._isMounted && this.setState({ retrievedData });
});
};
Two problems with that:
It updates an object stored in your state directly, which is something you must never do with React.
It replaces the entire retrievedData object with one that may well be stale.
Instead:
fetchDataByName = name => {
// *** No `retrievedData` here
const { fetcher } = this.props;
const fetch = _.find(fetcher, { name });
if (typeof fetch === "undefined") {
throw new Error(`Fetch with ${name} cannot be found in fetcher`);
}
this.fetchData(fetch, (error, data) => {
if (this._isMounted) { // ***
this.setState(({retrievedData}) => ( // ***
{ retrievedData: {...retrievedData, [name]: data} } // ***
); // ***
} // ***
});
};
That removes the in-place mutation of the object with spread, and uses an up-to-date version of retrievedData by using the callback version of setState.

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