AsyncStorage.getItem returns undefined : React Native - reactjs

Codeflow is-
I am checking if an entry called listobject exists in the AsyncStorage.
If it doesn't exist, then, I create an object, add few attributes and set the store. I get the store to obj as I have to compare in the next if condition.
If the listobject entry already exists(2nd time), then, it directly comes to the 2nd block, and compares. (The reason I get values to obj in 1st step is because I can have a common obj.data.isdirty condition.
Here is my code-
AsyncStorage.getItem('listobject').then((obj) => {
if(obj == undefined)
{
var obj1 ={};
obj1.data ={};
obj1.data.isdirty = true;
console.log("obj1 = "+ JSON.stringify(obj1));
AsyncStorage.setItem('listobject',obj1);
obj = AsyncStorage.getItem('listobject');
console.log("obj = "+ JSON.stringify(obj));
}
if(obj.data.isdirty)
{
obj.data.isdirty = false;
AsyncStorage.setItem('listobject',JSON.stringify(obj));
return AsyncStorage.getItem('listobject');
}
}).done();
I have 2 questions which are the outcome of the same issue-
Logs. I am setting obj1 and getting the same value for obj (so that I can compare the next if condition). Why am I not able to get the same value that I have set?
12-03 00:27:56.281 32598-487/com.abc D/ReactNativeJS: 'obj1 = {"data":{"isdirty":true}}'
12-03 00:27:56.286 32598-487/com.abc D/ReactNativeJS: 'obj = {"_37":0,"_12":null,"_59":[]}'
This is the end result of the above logs. I am getting that list.data.isdirty is undefined. I guess that because the JSON format I am accessing does not exist in obj i.e., obj.data.isdirty doesn't exist. So, how do I overcome this?
undefined is not an object (evaluating 'list.data.isdirty');
Please tell me what am I doing wrong?

I actually copied the object from one to another. It worked.
AsyncStorage.getItem('listobject').then((obj) => {
if(obj == undefined)
{
var obj1 ={};
obj1.data ={};
obj1.data.isdirty = true;
console.log("obj1 = "+ JSON.stringify(obj1));
AsyncStorage.setItem('listobject',obj1);
obj = obj1; //THIS IS WHAT I DID!
console.log("obj = "+ JSON.stringify(obj));
}
if(obj.data.isdirty)
{
obj.data.isdirty = false;
AsyncStorage.setItem('listobject',JSON.stringify(obj));
return AsyncStorage.getItem('listobject');
}
}).done();

I'm not quite following the entire question I do however see an issue with the use AsyncStorage. Going by the name, Async implies that the operations are asynchronous. So when you do getItem(key), you should either pass in a callback or use the Promise object it returns as you are doing in the first line of code.
obj = AsyncStorage.getItem('listobject');
console.log("obj = "+ JSON.stringify(obj));
obj is going to be the Promise in this case.
Then if you check on obj for the presence of a data and isDirty child property, they will not exist on the Promise.

Sometimes while doing console.log(AsyncStorage.getItem('Soomekey')) you will be getting undefined as you can't directly pull values from the AsyncStorage as returns a promise so what you should be writing is
const SomeFunction = async () => {
try {
const value = await AsyncStorage.getItem('somekey');
console.log(value);
} catch (err) {
console.log(err);
}
}

Related

Can anyone explain why my state is getting updated even when i dont set it manually

So I just spent an hour debugging this code and finally got it to work, but I would want to know why this happened in the first place. I have a function that takes a value from my state, operates on it and saves the output in another variable in the state. This is the fuction:
getFolderNames = async () => {
const promises = this.state.rows.map(async item => {
if (item[".tag"] == "folder" && item.name.length > 20) {
item.name = await getFolderName(item.name);
return item;
} else return item;
});
const result = await Promise.all(promises);
this.setState({
rowsToDisplay: result
});
};
when i run this function, it was updating both the rows and rowsToDisplay to the result variable when i was only calling setState on only one of them.
Changing the function as below solves the issue but I would like to know why.
getFolderNames = async () => {
const promises = this.state.rows.map(async item => {
if (item[".tag"] == "folder" && item.name.length > 20) {
let item2 = {
...item
};
item2.name = await getFolderName(item.name);
return item2;
} else return item;
});
const result = await Promise.all(promises);
this.setState({
rowsToDisplay: result
});
};
It's because of how JavaScript handles variables. When you set a variable to an array or object, it doesn't make a new object but rather just references the original array/object.
As such, if you set a variable to equal some object, and then set a property of that variable, the original object will also be updated. Check this snippet for an example.
var foo = {changed: false};
var bar = foo;
bar.changed = true;
console.log("foo", foo.changed)
console.log("bar", bar.changed)
You can read more about the subject here: https://codeburst.io/explaining-value-vs-reference-in-javascript-647a975e12a0
I hope this helps you in the future, since I know I also spent many hours banging my head against exactly the sort of cases you described in your original question.

Can't get the data from array react and Firestore

How can I access the value exist from an array? I think I didn't pass the array inside? Any help or advice
var isExist = this.props.isFavorite(this.props.code);
console.log(isExist)
I have this variable isExist containing the response from console below.
[]
client: [id: "LvR05w9v9xrC3r4V1W8g", exist: true]
length: 1
_proto_:Array(0)
How can I access the exist in my array? When I tried isExist[0].exist I'm getting an error. Any help?
isExist.exist = Undefined
isExist[0].exist = TypeError: Cannot read property 'exist' of undefined
favorite method where I am accessing and pushing data to the array
export const isFavorite = (data) => dispatch => {
let exist = [];
var clientQuery = firebase.firestore().collection(path).where('client_id', '==', data);
clientQuery.get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
var data = [];
data.id = doc.id;
data.exist = doc.exists;
exist.push(data)
});
});
return exist;
}
isFavorite returns a function which takes one argument dispatch and returns exist array. You seem to use async code to populate exist array. So when that function returns exist is an empty array []. You either need to continue using promises or use await. And you need to call the function returned by isFavorite.
If this.props.isFavorite and const isFavorite are not the same then add the code for this.props.isFavorite please.
You're creating an array Object. Then the array object {data}[]. So the problem is, the data is actually not only an array but also an object.
Try doing this.
var data;
data.id = doc.id;
data.exist = doc.exist;
exist.push(data);
Now you will have exist data that would be an array of Object.
Then iterate from it.
exist[0].id;
//or try
exist[0].data.id;
//Depends on how you implement your data.
Since client array doesn’t contain object with keys and values I would recommend you to try with array of index with split() to get id value and exist value from array like
Like
var isExist = this.props.isFavorite(this.props.code);
var id = isExist.client[0];
var exist = isExist.client[1];
var idValue = id ? id.split(': '): '';
console.log(idValue);
const existValue = exist ? exist.split(': '): false;
console.log(existValue);
And here change data = []; array to data ={}; object
querySnapshot.forEach((doc) => {
var data = {};
data.id = doc.id;
data.exist = doc.exists;
exist.push(data)
});

Extending $firebaseArray with an extended $firebaseObject

Trying to cut down code repetition, I've set up a $firebaseArray extension as follows:
var listUsersFactory = $firebaseArray.$extend({
$$added: function (snap) {
return new Customer(snap);
},
$$updated: function (snap) {
var c = this.$getRecord(snap.key);
var updated = c.updated(snap);
return updated;
},
});
and the Customer code:
function Customer(snap) {
this.$id = snap.key;
this.updated(snap);
}
Customer.prototype = {
updated: function(snap) {
var oldData = angular.extend({}, this.data);
this.data = snap.val();
// checks and such
}
}
This works wonders when loading, showing and saving a list of customers, and I'm satisfied with it.
Now, the problem lies in retrieving a single customer and its detail page, because the Customer object isn't an extension of $fireObject and is therefore lacking a $save method
Single customer loading:
customersRef.once("value", function(snapshot) {
if(snapshot.child(uuid).exists())
{
customersFactory.customerDetails = new Customer(snapshot.child(uuid));
return deferred.resolve();
}
}
but when I call customersFactory.customerDetails.$save() I get an error
How can I extend my class so that it works for both array and single object uses?
I couldn't find a way to do this, so I ended up using the $firebaseArray and getting single records off that to pass as details, in case anyone's wondering

Add variable to an existing json object

request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
var time = Math.floor((Date.now() - Date.parse($scope.cases[i].date_case_modified))/(60000*60*24));
$scope.cases.duration.push(time);
}
});
Inside the controller I am trying to tack on the cases.duration onto the cases object but it wont add it onto the object that is returned. Any ideas?
I think you just need to introduce a forEach as shown here:
request = myService.getCases();
request.then(
function(payload) {
$scope.cases = payload.data;
angular.forEach($scope.cases, function (el) {
var time = Math.floor((Date.now() - Date.parse(el.date_case_modified))/(60000*60*24));
el.duration = time;
});
}
});
Hope this helps

How to extend returned objects in the list returned by $asArray?

I'm having trouble decorate the objects in my list returned by $asArray in angularfire with a new method (not decorating the array itself).
The angularfire documentation seems to suggest that the right way to do this is to override the $$added method in the factory for $FirebaseArray, returning a new object that either encapsulates or extends the snapshot that gets passed in to that method. From the documentation:
// an object to return in our JokeFactory
app.factory("Joke", function($firebaseUtils) {
function Joke(snapshot) {
this.$id = snapshot.name();
this.update(snapshot);
}
Joke.prototype = {
update: function(snapshot) {
// apply changes to this.data instead of directly on `this`
this.data = snapshot.val();
},
makeJoke: function() {
alert("Why did the " + this.animal + " cross the " + this.obstacle + "?");
},
toJSON: function() {
// since we didn't store our data directly on `this`, we need to return
// it in parsed format. We can use the util function to remove $ variables
// and get it ready to ship
return $firebaseUtils.toJSON(this.data);
}
};
return Joke;
});
app.factory("JokeFactory", function($FirebaseArray, Joke) {
return $FirebaseArray.$extendFactory({
// change the added behavior to return Joke objects
$$added: function(snap) {
return new Joke(snap);
},
// override the update behavior to call Joke.update()
$$updated: function(snap) {
this.$getRecord(snap.name()).update(snap);
}
});
});
However, when I do this in my code, nothing ever gets added to the array, although I can see from outputting to the console that it is getting called.
var printMessageObjConstructor = function(snap) {
this.$id = snap.name();
this.snapshot = snap;
this.$update = function(snap) {
this.snapshot = snap;
};
this.printMessage = function() {
return this.author + "'s question is: " + this.body;
};
};
var ref = new Firebase("https://danculley-test.firebaseio.com/questions");
//What Am I Doing Wrong Here?
var arrayFactory = $FirebaseArray.$extendFactory({
$$added: function(snap, prevChild) {
var x = new printMessageObjConstructor(snap);
console.log("I am being called from FirebaseDecoratedCtlOverloadAddedinNewObj.");
return x;
},
$createObject: function(snap) {
return new printMessageObjConstructor(snap);
},
$$updated: function(snap) {
var i = this.$indexFor(snap.name());
var q = this.$list[i];
q.$update(snap);
}
});
var sync = $firebase(ref, {arrayFactory:arrayFactory});
var list = sync.$asArray();
list.$loaded(function(list) {
$scope.questions = list;
});
I've set up a new plunk stripped down to show the issue with a couple other use cases that I've tried. (The actual method I'm adding is more complex and isn't related to the view, but I wanted to do something simple to reproduce the issue.)
I think the issue is that I don't quite understand what exactly $$added is supposed to return, or what additional behavior beside returning the value to be stored $$added is supposed to have. There also doesn't really seem to be an $$added on the prototype or on $FirebaseArray to call as a super to get the default behavior. Can someone point me in the right direction?
UPDATE
For the benefit of others, after reviewing the like that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}
For the benefit of others, after reviewing the link that Kato posted, I was able to solve the issue by adding the following, almost all copied directly from the source except for the commented line below.
$$added: function(snap, prevChild) {
var i = this.$indexFor(snap.name());
if( i === -1 ) {
var rec = snap.val();
if( !angular.isObject(rec) ) {
rec = { $value: rec };
}
rec.$id = snap.name();
rec.$priority = snap.getPriority();
$firebaseUtils.applyDefaults(rec, this.$$defaults);
//This is the line that I added to what I copied from the source
angular.extend(rec, printMessageObj);
this._process('child_added', rec, prevChild);
}
}

Resources