Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I want to save houses in an array using AsyncStorage. Each array element represents a house Object. And each house object has a latitude, longitude and a house number. I am not sure how to go about representing this. It seems to me that AsyncStorage is not well-suited for saving dynamic objects that can be updated programmatically.
I want to be able to add more houses and delete some houses. Basically what I am trying to do is bookmark some houses and delete them from the bookmarks when the user clicks.
Can anyone help ?
AsyncStorage is absolutely perfect for this.
Starting with the structure of your houses, I would create an Array which stores objects representing an individual house.
const houses = [
{
number: 1,
latitude: 51.5033,
longitude: -0.119519
}
]
Saving to AsyncStorage
When you want to write your collection to AsyncStorage, you would do so like this:
AsyncStorage
.setItem('#houses', JSON.stringify(houses))
.then(houses => console.log(houses)
static setItem(key: string, value: string, callback?: ?(error: ?Error) => void)
You can also use async/await if your project is set up to support it.
Reading from AsyncStorage
Reading your collection back from AsyncStorage is simply done via:
AsyncStorage
.getItem('#houses')
.then(houses => console.log(JSON.parse(houses)))
You could set the response in your state, do as you please.
Deleting a specific house
This depends entirely on how you set your app up. Are you going to map through each house and create a list component for example?
(Sorry about losing the border on the right)
If so, you could:
houses.map(house => (
<View>
<Text>Number: {house.number}</Text>
<Text>Latitude: {house.latitude}</Text>
<Text>Longitude: {house.longitude</Text>
<Button onPress={() => this.deleteHouse(house.number)}/>
</View>
));
Then create a deleteHouse function which will handle it.
const deleteHouse = (number) => {
const { houses } = this.state; // Assuming you set state as previously mentioned
const newHouses = houses.filter((house) => house.number != number);
this.setState({houses: newHouses}, () => saveHouses);
}
And finally a saveHouses to sync it back to AsyncStorage. The function will clear AsyncStorage and then save the new houses.
const saveHouses = () => {
const { houses } = state;
AsyncStorage
.removeItem('#houses')
.then(() => {
AsyncStorage
.setItem('#houses', JSON.stringify(houses))
.then(houses => console.log(houses)
});
}
You need a Sqlite alternative like Realm, it's free, fast and easy to code, with this you will save tons of hours of work, also as you need, you can make queries, update and delete objects.
Related
Hello there's something I really do not understand because it does not make any sense at all, I have 2 pieces of code that are EXACTLY the same only difference is the variables I use to find the collections and one does persist even after refreshing and the other one doesn't here are the pieces of code:
Students list one:
const [estudiantes, setEstudiantes] = useState([]);
const estudiantesRef = db.collection("usuarios").doc(user.uid).collection("estudiantes")
useEffect(() => {
estudiantesRef.onSnapshot(snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setEstudiantes(tempData);
})
}, []);
console.log(user.uid)
console.log(estudiantes)
Books list one:
const [productos, setProductos] = useState([]);
const productosRef = db.collection('libros');
useEffect(() => {
productosRef
.orderBy('grado')
.onSnapshot( snapshot => {
const tempData = [];
snapshot.forEach((doc) => {
const data = doc.data();
tempData.push(data);
});
setProductos(tempData);
})
}, []);
Gif student
Gif books list
Update: The user.uid is ALWAYS present regardless of the refresh however the data stored in estudiantes disappears when I refresh and that's not good lol. Why does the data disappear ? I test it by only loading 1 collection and it doesn't disappear so why does it disappear when it goes to multiple collections and how I can fix it ?
Before Refresh
After Refresh
The two pieces of code might seem to do exactly the same thing, but the reality is, they are not.
estudiantesRef references: collection > document > collection
productosRef references: collection
Naturally you'd expect productosRef to return faster as it's only doing one lookup, but it also depends on the number of records in each collection, etc. So you should double check this. Also, any additional processing done on the data after retrieval will affect the time it takes to show.
My suggestion is you create a loading state (or use console.log) to double check you're getting back data when you expect to.
one does persist even after refreshing and the other one doesn't
Unless you're using something other than React.useState to store your state, then the data shouldn't be persisting between refreshes. This could be a result of local caching, or the page just loading quickly.
Not sure if you still need an answer, but:
For data to be kept on refresh, you should set your items in localStorage
const item= localStorage.setItem('keyValue', 'dataToPersist')
then get data by:
const item= localStorage.getItem('keyValue')
I just want to clarify that after a week or more of fighting with this issue, the answer wasn't because it was a nested collection
(Nested)
estudiantesRef references: collection > document > collection
(not nested)
productosRef references: collection
it was because the user from my code was being set back to null as soon as you refresh but if you wait like 2 seconds it changes back to his normal value. all I had to do is use user in the array dependency in my firebase useEffect.
However this means that the issue wasn't because it was a nested collection as someone else pointed out. just that, firebase can load data instantly (as long as there aren't that many values clearly) there shouldn't be an issue in loading just 1 piece of information.
I am working through a simple logical problem, but I cannot seem to have things work smoothly. Let me share my most convincing code experiment and then I'll share some thoughts.
useEffect(() => {
firebase
.firestore()
.collection("someCollection")
.orderBy("date", "desc")
.onSnapshot(docs => {
let documents = []
if (canGetUpdatesFromFirestore.current) {
docs.forEach((doc) => {
documents.push(doc.data())
})
if(documents.length > 3) {
documents.splice(4, 0, {questionPostId: 0})
documents.splice(5, 0, {questionPostId: 1})
}
setAllQuestions(documents)
setUsers(documents)
}
})
if (searchValue.length > 2) {
canGetUpdatesFromFirestore.current = false;
functions.searchForSearchVal(searchValue, "Sexuality")
.then((result) => {
setAllQuestions(result);
})
} else {
canGetUpdatesFromFirestore.current = true
}
}, [searchValue])
function setUsers(docs){
let arrFinal = []
let copyOfAllQuestions = ""
for(let i = 0; i< docs.length; i++) {
console.log("HERE")
if (docs[i].postedBy) {
docs[i].ref.get().then(userFire => {
copyOfAllQuestions = {
...allQuestions,
...{hasPremium: userFire.data().hasPremium}
}
})
arrFinal.push(copyOfAllQuestions)
}
}
setAllQuestions(arrFinal)
}
Let me share some of my current state and what I am trying to accomplish.
I have a that display allQuestions. Each question data has a ref to its user document in firestore. For each question I need to check if that user hasPremium. How should I go about doing that the correct way?
The problem currently is that I can get the data from my Users collection through the ref, but I have to refresh my state in order for it all to show.
Could someone help me get on the right path / think correctly on this one please?
One approach that I put forward is to embrace data denormalization. That is, rather than putting references to other documents (Users) inside of the Questions document, put all the relevant user information directly into the Questions document.
This is antithetical to SQL database approaches, but that's okay because Firestore is "NoSQL". Embrace the anti-SQL-idity!!
Essentially, in your Question document you want to copy in whatever information is required in your app when working with a Question, and avoid doing "joins" by fetching other documents. You don't need to copy in all of the User document into a Question document - just the elements needed when your app is working with a Question.
For example, maybe in the question all you need is:
question: {
name: ...,
type: ...,
lastUpdated: ...,
postedBy: {
email: ...,
displayName: ...,
avatarUrl: ...,
hasPremium: true,
}
}
With data duplicated, you often need a mechanism to keep duplicate data up-to-date from its "source". So you might consider a Cloud Function trigger for onUpdate() of User documents, and when a relevant value is modified (email, displayName, avatarUrl, and/or hasPremium) then you would loop through all questions that are postedBy that user and update accordingly.
The rules-of-thumb here are:
all data needed for one screen/function in your app goes into a SINGLE document
NoSQL document stores are used where reads are frequent and writes are infrequent
NoSQL data stores (typically) do not have "joins" - so don't design your app to require them (which is what your code above is doing: joining Question and Users)
often you don't care about updating ALL instances of duplicated data (e.g. if a user updates their displayName today, should you update a Question they posted 3 years ago? -- different apps/business needs will give different answers)
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 3 years ago.
Improve this question
I am learning react and get stuck in the following line of code:
const { favourites } = this.state
Can someone please help me?
that isn't React-specific, it is a JavaScript (ES6) feature called destructuring:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring
What this means is that the const favorites is equal to the value of favorites in the state of the component. So if the value of favorites is 22 in the state, the const favorite is equal to 22.
const { favourites } = this.state
is same as
const favourites = this.state.favourites
Also, an important note is that by using const, you're making the state.favorites prop read-only. Also, you can use it for any object. Let's say I had an employee object with employeeId, lastName, firstName props, etc. Let's say I get an array of employee objects, I can use this construct to greatly simplify the code.
render() {
const {UserName, EmailAddress, AccountCreated, PasswordChanged, AccountName,
Locked, Enabled} = this.props.ewdsUser;
return (
<tr>
<td>{UserName}</td>
<td>{EmailAddress}</td>
<td>{AccountCreated}</td>
<td>{PasswordChanged}</td>
<td>{Locked}</td>
<td>{Enabled}</td>
This prevents me from having to do this:
render() {
return (
<tr>
<td>{this.props.UserName}</td>
<td>{this.props.EmailAddress}</td>
<td>{this.props.AccountCreated}</td>
<td>{this.props.PasswordChanged}</td>
<td>{this.props.Locked}</td>
<td>{this.props.Enabled}</td>
imagine this scenario:
You have a list with lets say 100 items, a user favorites 12 items from the list.
The user now wants these 12 items displayed in a new list with the up to date data (if data changes from the original list/node)
What would be the best way to accomplish this without having to denormalize all of the data for each item?
is there a way to orderByChild().equalTo(multiple items)
(the multiple items being the favorited items)
Currently, I am successfully displaying the favorites in a new list but I am pushing all the data to the users node with the favorites, problem is when i change data, their data from favorites wont change.
note - these changes are made manually in the db and cannot be changed by the user (meta data that can potentially change)
UPDATE
I'm trying to achieve this now with cloud functions. I am trying to update the item but I can't seem to get it working.
Here is my code:
exports.itemUpdate = functions.database
.ref('/items/{itemId}')
.onUpdate((change, context) => {
const before = change.before.val(); // DataSnapshot after the change
const after = change.after.val(); // DataSnapshot after the change
console.log(after);
if (before.effects === after.effects) {
console.log('effects didnt change')
return null;
}
const ref = admin.database().ref('users')
.orderByChild('likedItems')
.equalTo(before.title);
return ref.update(after);
});
I'm not to sure what I am doing wrong here.
Cheers!
There isn't a way to do multiple items in the orderByChild, denormalising in NoSQL is fine its just about keeping it in sync like you mention. I would recommend using a Cloud Function which will help you to keep things in sync.
Another option is to use Firestore instead of the Realtime Database as it has better querying capabilities where you could store a users id in the document and using an array contains filter you could get all the users posts.
The below is the start of a Cloud function for a realtime database trigger for an update of an item.
exports.itemUpdate = functions.database.ref('/items/{itemId}')
.onUpdate((snap, context) => {
// Query your users node for matching items and update them
});
React + Redux recommend saving data normalized and using selectors to get derived data. So in our store we save Users and Tags which have a many to many relationship.
type Store = {
entities: {
users: User[];
tags: Tag[];
userTagMapping: { userId: string, tagId: string }[]
}
}
And in our view we want to show some derived data for this many to many relation-ship. For example we want to calculate the total users with some tag or the online-users with some tag. Right now we solved this using rselect. The only problem is that calculating these things becomes quite tedious. For example we have a selector which returns a map from a tag-id to a list of users to show which users belong to this tag (and vice versa a selector from user-id to tag list).
const selectUsersPerTag = createSelector(
selectUsers, selectTags, selectUserTagMapping,
(users, tags, mapping) => {
let result = {};
for (const tag on tags) {
const isUserMappedToTag = user => ({userId, tagId}) => userId == user.id && tagId === tag.id
result[tag.id] = users.filter(user => mapping.some(isUserMappedToTag(user)))
}
return result
}
)
and in my opinion this looks quite ugly and is a bit too hard to read for my taste.
My questions are:
Are we understanding the recommendations correctly (to use normalization and selectors)?
Is using a map the correct way to process our data and show it in the view or is there a better way? I am asking because this basically copies our data (slightly modified) many times into the props of our React components
Is there a nicer way to do this mapping (which is basically a SQL like join)? Because I really don't like this imperative approach and would find a functional one much nicer.