Why does this hook call return undefined when I simply change the variable name? - javascript

I'm running an API call via redux - in the tutorial, I am following, they use the variable name "sales" to store the data. Following along, I kept getting undefined, and after some troubleshooting, it appears that the only way for me to get any data out of this API call is to save the result in a variable named exactly "data".
// Correctly retrieves and logs the data
const { data, isFetching } = useGetSalesQuery();
console.log(data);
// Returns "undefined" every time
const { anythingElse, isFetching } = useGetSalesQuery();
console.log(anythingElse);
data is not defined anywhere else within this component. So what's going on here? Was Redux updated to force us to always use the name "data"? This is doing my head in.

useGetSalesQuery returns an object that has data and isFetching. Attempting to access an arbitrary field from that object will get you undefined. What's going on in this component is that you are defining a variable data and assign it the value from the field data that is returned from useGetSalesQuery
See javascript's destructuring assignment

Related

Uncaught TypeError: Cannot read properties of undefined (reading 'dailyconfirmed')

what's problem with my code i am getting error while accesing mydata.dailyconfirmed after page reloading?
const[state,setState]=useState({});
async function getApiIndia1(){
const temp=await fetch('https://data.covid19india.org/data.json')
const mydata=await temp.json();
setState(mydata)
}
useEffect(() => {
getApiIndia1();
}, [])
You cannot access mydata outside of getApiIndia1 (which I assume you are trying to do). It is only defined in that block, which is the reason why you're using state (essentially to save the required data globally). mydata can't be found and that's why it's throwing an error.
What you should do instead is access state, which is where you're storing that data.
But that will still throw an undefined. That's because of the issue that previous answers have already highlighted. You need to access state.cases_time_series.dailyconfirmed. That should give you the data you want.
It's always a good idea to check API responses carefully. ;)
Update the setState
setState(mydata.cases_time_series)
and when you wish to find dailyconfirmed, loop over your state which is basically an array of object.
state.map((dailyconf)=>{
if(dailyconf?.dailyconfirmed) console.log(dailyconf?.dailyconfirmed)
})

Can't access array after passing it to state, but can access it before

I have an pseudo-object is inside my state. I've been able to access through a couple layers, but when I reach the array inside the object Im getting undefined errors.
UPDATE: Its something wrong with how I pass lambdaReturnObject to the state which isn't letting me access the array, tested with lambdaReturnObject.campaigns[0].campaignName and it worked.
handleSearch() {
//data to use to query backend
let campaignId = this.refs.campaignInput.value
let marketplace = this.refs.marketplaceInput.value
//using local copy of backend data, production should call backend fo this instead
let lambdaReturn = "{\"advertiser\":{\"advertiserId\":\"1\",\"enforcedBudget\":0.1},\"campaigns\":[{\"campaignID\":\"1\",\"campaignName\":\"fake\",\"createDate\":11111,\"creationDate\":1111,\"startDate\":1111,\"endDate\":1111,\"dailyBudget\":0.1,\"internal\":{\"budgetCurrencyCode\":\"USD\",\"inBudget\":true},\"enforcedBudget\":0.1,\"budgetCurrencyCode\":\"USD\",\"budgetPacingStrategy\":\"asp\",\"budgetType\":\"averageDaily\",\"status\":\"enables\",\"internalStatus\":\"enabled\"}],\"campaignID\":\"1\"}"
let lambdaReturnObject = JSON.parse(lambdaReturn)
this.setState({
apiData: lambdaReturnObject
})
}
When I try and go to the array inside, I get the following error
<h3>Campaigns :{console.log(this.state.apiData.campaigns[0].campaignName)}</h3>
Cannot read property '0' of undefined
This means I am accessing it the wrong way, but I looked at other posts (Accessing Object inside Array) and I thought that this was right. Though I am definitely wrong or else I wouldn't be writing this.
JSON.parse() is synchronous function, so set state wont be called till, JSON.parse() executes completely and returns the object.
Still You can try following
Call JSON.parse() using a try-catch block like below and see if it works. Also it is error free way of parsing your stringified objects.
try {
let lambdaReturnObject = JSON.parse(lambdaReturn)
this.setState({
apiData: lambdaReturnObject
})object
}
catch (err) {
// Do error handling here.
}
Use optional chaining, and try to access your object like this.state.apiData.campaigns?.[0].campaignName; this won't give error even if compaigns is undefined.
Refer : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining
Depending on what is happening. The call to get campaigns[0] is getting resolved before the API call finishes. You can try a promise or async await to make sure the object is retrieved from the API call before you attempt to access it.
Do you happen to have a code snippet of is being used to get the data?
The error was that render() calls right as the page is booted up. At that time the object is not stored in my state so trying to call
this.state.objects.innerObject.property
would fail because I declared objects in my state, innerObject could be counted as null before the object is actually loaded in so it wouldn't throw an error. But property would throw an error because innerObject as we know, is null.
Fix by using an if statement before rendering the page to see if the object is actually loaded in. If not render just default empty JSX.

Why does data from .subscribe() first return an empty object before filling the object with data?

I'm working with Observables to get data from the backend. In my function I'm subscribing to my observable and when I console.log() the data that is passed back it appears to return twice. First an empty object and then later the data I'm expecting.
This is causing a problem for me because I'm trying to use a for...in loop to compare the keys of the data with the keys of another object so I can match the values. I get a TypeError: Cannot read property '0' of undefined
because the data first returns an empty object. This is confusing to me because I'm doing console.log() inside the subscribe's callback method.
Isn't the whole point of the callback method to wait until the data has arrived?
I've tried a callback function as well as putting the for...in directly inside the subscribe and neither work because the object returns empty first. What am I doing wrong?
this.memberService.memberDetails.subscribe(member => {
this.member = member;
this.member_id = this.authService.loggedInUser.app_user_data.member_id;
this.parseAddress();
console.log('member subscribe', this.member);
this.formData();
});
// UPDATE: MemberService code
private _memberDetails = new BehaviorSubject<any>({});
public get memberDetails() {
return this._memberDetails.asObservable();
}
// Notice the console.log() has fired twice
formData() {
for (const flatProfileKey in this.profileData['permanent_address'][0]) {
for (const key in this.member['addresses'][0]) {
if (flatProfileKey === key) {
this.profileData[key] = this.flatProfile[key];
console.log('profileData perament_address', this.profileData['permanent_address'][0])
}
}
}
}
// If I try to loop through the data it returns an undefined error presumably because the subscribe first returns an empty object so there is nothing to loop through
This is expected as you are setting initial value to BehaviorSubject to empty object here:
private _memberDetails = new BehaviorSubject<any>({});
If you don't need an initial value you can consider using Subject instead of BehaviorSubject.
Read more about BehaviorSubject here: https://www.learnrxjs.io/subjects/behaviorsubject.html
This is working as expected.
Behavior subject will re-emit the last emitted value or the default value if no previous value has been emitted.
Now, see that you are providing the default value to the BehaviorSubject as an empty object. Since you are passing a default value to it, it will emit this default value to all the subscribers.
And when you retrieve the data and change the value of the BehaviorSubject then again it will emit the new data to the subscribers.
Also, if you are thinking to pass nothing as a default value to BehaviourSubject, you cannot do that.
Here, you have two options you can do:
You can add an additional if to check if the value emitted is default value {} or not. And according to that you handle the stuff. Here's the code:
formData() {
if(this.profileData['permanent_address']) {
// Data fetched, do something
for (const flatProfileKey in this.profileData['permanent_address'][0]) {
}
}
else {
// If data is not fetched, do something if you want to
}
}
You can use Subject instead of BehaviorSubject that doesn't need a default value. Since it doesn't have a value, it will not emit it before fetching the data. Just change below line:
private _memberDetails = new BehaviorSubject<any>({})
to
private _memberDetails = new Subject<any>()
And this will work as you expect it to work.
Hope this helps.

Parsing JSON objects array and displaying it in ReactJS

I've been facing a weird issue lately with my React App. I'm trying to parse a JSON object that contains arrays with data. The data is something like this:
{"Place":"San Francisco","Country":"USA", "Author":{"Name":"xyz", "Title":"View from the stars"}, "Year":"2018", "Places":[{"Price":"Free", "Address":"sfo"},{"Price":"$10","Address":"museum"}] }
The data contains multiple arrays like the Author example I've just shown. I have a function that fetches this data from a URL. I'm calling that function in componentDidMount. The function takes the data i.e responseJson and then stores it in an empty array that I've set called result using setState. In my state I have result as result:[]. My code for this would look something like this:
this.setState({result:responseJson})
Now, when I've been trying to access say Author Name from result I get an error. So something like this:
{this.state.result.Author.Name}
I'm doing this in a function that I'm using to display stuff. I'm calling this function in my return of my render function. I get an error stating :
TypeError:Cannot read property 'Name' of undefined. I get the same error if I try for anything that goes a level below inside. If I display {this.state.result.Place} or {this.state.result.Country} it's all good. But if I try,say {this.state.result.Author.Title} or {this.state.result.Places[0].Price} it gives me the same error.
Surprising thing is I've parsed this same object in a different component of mine and got no errors there. Could anyone please explain me why this is happening?
If I store the individual element while I setState in my fetch call function, I can display it. For example:
{result:responseJson,
AuthorName:responseJson.Author.Name
}
Then I'm able to go ahead and use it as {this.state.AuthorName}.
Please help me find a solution to this problem. Thanks in advance!
It could be that your state object is empty on the first render, and only updated with the data from the API after the request has completed (i.e. after the first render). The Name and Place properties don't throw an error, as they probably resolve to undefined.
Try putting an if block in your render method to check if the results have been loaded, and display a loading indicator if they haven't.
I'm guessing your initial state is something like this:
{ results: {} }
It's difficult to say without seeing more code.
[EDIT]: adding notes from chat
Data isn't available on first render. The sequence of events rendering this component looks something like this:
Instantiate component, the initial state is set to { results: [] }
Component is mounted, API call is triggered (note, this asynchronous, and doesn't return data yet)
Render method is called for the 1st time. This happens BEFORE the data is returned from the API request, so the state object is still {results: [] }. Any attempts to get authors at this point will throw an error as results.Authors is undefined
API request returns data, setState call updates state to { results: { name: 'test', author: [...] } }. This will trigger a re-render of the component
Render method is called for the 2nd time. Only at this point do you have data in the state object.
If this state evolves, means it is changed at componentDidMount, or after a fetch or whatever, chances are that your state is first empty, then it fills with your data.
So the reason you are getting this error, is simply that react tries to get this.state.result.Author.Name before this.state.result.Author even exists.
To get it, first test this.state.result.Author, and if indeed there's something there, then get Author.Name like this.
render(){
return(
<div>
{this.state.result.Author ? this.state.result.Author.Name : 'not ready yet'}
</div>
);
}
[EDIT] I'll answer the comment here:
It's just because they are at a higher level in the object.
this.state.result will always return something, even false if there is no result key in your state (no result key in your constructor for instance when the component mounts).
this.state.result.Country will show the same error if result is not a key of your state in your constructor. However, if result is defined in your constructor, then it will be false at first, then become the data when the new state populates.
this.state.result.Author.Name is again one level deeper...
So to avoid it, you would have to define your whole "schema" in the constructor (bad practice in my opinion). This below would throw no error when getting this.state.result.Author.Name if I'm not mistaken. It would first return false, then the value when available.
constructor(props){
super(props);
this.state={
result: {
Author: {}
}
}
}

What happens to property values on an object in JavaScript when its property values change during asynchronous function calls?

I am wondering what happens to a javascript object that is passed to an asynchronous function (like a database save), and then immediately has its property's value changed.
The reason I am interested in this is that the asynchronous code takes a long time to finish and so waiting for it to callback would take longer than I would like to respond.
Take the following code:
function asyncDatabaseSave(user) {
// Save the user asynchronously in the database
// Do things that take a lot of time before calling back
}
app.get(function (req, res) {
// In real code user is an object that gets passed in.
user = {cart: [1, 2, 3]};
// What is the value of user.cart going to be when it reaches the databaseSave call?
// At the time of the function call, user.cart has options but it is set to null immediately after
asyncDatabaseSave(user);
user.cart = null;
res.json(user);
})
I think that the "correct" way to ensure that the user object was saved with the cart not null and then clear it would be to pass a callback into asyncDatabaseSave to set it to null after the save.
However I am interested in calling res.json as soon as possible. I have also experimented with this code:
app.get(function (req, res) {
var items = user.cart;
user.cart = null;
res.json(user);
asyncDatabaseSave(user, items);
})
This way items is stored as a separate var and passed into a slightly-modified database save function.
What happens to user.cart when asyncDatabaseSave is called and then it is set to null immediately after?
If I am trying to save user with cart, but also return the user with cart set to null what is the best way to do this?
What happens to user.cart when asyncDatabaseSave is called and then it
is set to null immediately after?
You'd have to know what actually happens in the async database call to know whether it's safe to modify the passed in object immediately after making the call or not. There are many cases where the operative parts of the user object would already be copied into native code and sent off to the database before asyncDatabaseSave() returns so further modifying the user object would not affect the async call at all.
But, there are situations where you can't assume that, particular if asyncDatabaseSave() is actually made up of several async calls and some Javascript runs between them (such as opening the db, then writing to the db. In those cases, modifying the user object where you are could affect things.
If I am trying to save user with cart, but also return the user with
cart set to null what is the best way to do this?
So, to be safe, don't modify the user object right after asyncDatabaseSave(user).
If you really just want to call res.json(user) as fast as possible, then you can just make a copy of the user object, modify that copy and then use each separate copy in your two async operations. That's the general answer that works in all cases and it's the safe way to do things.
If the whole cart object is not needed in asyncDatabaseSave(), then picking out just the parts that are needed and passing those (like your last suggestion), allows you to freely assign cart properties afterwards without any risk. You do have to be careful that you aren't reaching into the cart object and changing objects in the cart object because that might be changing the same objects that you passed to asyncDatabaseSave(), but you can certainly assign properties to the cart object and that will not affect asyncDatabaseSave() if you didn't pass it the cart object.
Here's a simple example to show how it's not safe to assume you can modify the object you passed in:
function someAsyncSave(u) {
setTimeout(function() {
log(u.name)
}, 50);
}
var user = {name: "Joe"};
someAsyncSave(user);
user.name = "Alice";
<script src="http://files.the-friend-family.com/log.js"></script>
Run the snippet. It will log "Alice", even though the property was "Joe" when someAsyncSave() was called.
Even though the user object had user.name = "Joe" when it was passed to someAsyncSave(), by the time someAsyncSave() actually uses the property, it has already been changed by the outer code.

Categories

Resources