How to get an object from firebase in react native - reactjs

I'm trying to read object data from firebase, but since I use the code below, some error occurred.
This is my method to read data from firebase.
function getMenuData() {
let newMenu = [];
MENUref.onSnapshot(querySnapshot => {
querySnapshot.forEach(doc => {
const menu = {
id: doc.id,
Name: doc.data().name,
desc: doc.data().desc,
url: Images[doc.data().name],
people: doc.data().people,
type: doc.data().type,
time: doc.data().time,
ingre: doc.data().ingre,
season: doc.data().season,
like: doc.data().like,
}
newMenu.push(menu)
});
setMenu(newMenu)
setDisplayOfData(newMenu)
// console.log('food', Menu)
});
}
Then pop out "Objects are not valid as a React child." error message.
After I do some research shows it need to done through map() method, but can it performed in the "menu: {}" ?
The goal is to read the object data from firebase and make it like this.
data structure
This is my firebase data structure.
Or some available approaches can do the same thing with the object data like this?
Thanks a lot!

In the JavaScript SDK there is something known as useCollectionData than can be imported from react-firebase-hooks/firestore then you would use something such as const [data] = useCollectionData(query); where query could be a reference to the firebase collection you are pointing to with filtering capabilities ex. firestore.collection('your-collection').orderBy('createdAt). This is the official method of handling firebase data which you can then simply use with the .map(item => <Item data={item}>)

Check how your menu is being rendered to the View.
Since the menu is an array of data from firebase, to render it to the view, you have to use the map() js method because objects or array cannot be converted to a React Child element.
e.g;
{menu.map((item) => {
return <Text>{item.Name}</Text>
})}

Related

Is there a way to save dynamic JSON into Mobx State Tree?

I was using Redux before and saving dynamic JSON into the store was quite easy. I was thinking, how can we do the same in the Mobx-State-Tree, without actually defining the model structures.
.model({
data:types.array(...)
)}
I was trying, but again I need to define the JSON array structure into it.
The whole idea for using mobx state tree is to define implicit data types into your application.
But if for some reason you want to save JSON without defining is to convert it into a string using JSON.stringify({...})
And the model will be actually a string
.model({
data:types.string
)}
Then you can use actions to save JSON into it
.actions((self) => ({
saveJSON(jsonData){
self.data = JSON.stringify(jsonData)
}
})
You can then use this JSON anywhere by calling store.getJson property with JSON.parse() using view
views((self) => ({
get getJson(){
return JSON.parse(self.data)
}
})
I needed to do this because I receive object of a well defined type, but that type contains a config field, that can be any json.
I managed to do it in MST by defining:
export const AnyJsonValue = types.union(
types.string,
types.number,
types.integer,
types.Date,
types.boolean,
types.map(types.late(() => JsonValue)),
types.array(types.late(() => JsonValue)),
types.undefined,
types.null
);
And then using it like this:
export const MyObject = types.model("MyObject", {
uuid: types.identifier,
name: types.string,
config: types.maybeNull(types.map(AnyJsonValue)),
...
});
If you have some restrictions, you can still set them.
For example, if you want config to be either null, or a dict with any json nested (so config can't be a literal or array, but can be a dict containing them), you can use the type: t.maybeNull(t.map(JsonValue)).

ReactJs : How to print data from console to web-page?

After a successful POST and GET query, the results of my operation are visible in the console of my React Dev Tools. How should I take those results, preferable create a table and render that table on my web-app itself?
Here is the GET request :
axios.get('http://localhost:5000/result')
.then((response) => {
console.log(response);
});
}
The format of results displayed on console is like this :
Let's say I want to create a table by traversing through the results with headers _id and Name. I know I should use the map function. But exactly where and how should I do that in my code?
You have a few options. You can make your call in componentDidMount, set the result in a state and then render that state.
componentDidMount() {
axios.get('http://localhost:5000/result')
.then((response) => {
this.setState({
data: response // maninpulate your response here
})
});
}
}
render() {
const { data } = this.state;
return this.renderTable(data) // this should return a table
}
Assuming you know the concept of 'useState' in react hooks. If not please have an understanding of it.
Long story short, useState is used to store the data in variables.
const [data, setData] = useState();
Instead of console.log(response); you set the response to the variable i.e; setData(response);
In html,
<table>
//some headers, if needed
//This will iterate your array of objects
{data.map((eachData) => (
<tr> <td>{eachData.id}</td>
<td>{eachData.name}</td>
....
</tr>
)
</table>
Please Note: The HTML part works for both class-based and function-based React components where as 'useState' is part of React hooks functionality, works only for function-based components.
I have created a small sample app where I have used react-table for displaying the data as a table. Also In this example I have added promise in order to simulate server fetching data from server. This you can replace with actual server call i.e., axis.get etc.
React-table provides a lot many features that might be helpful. Such as you can provide which columns you wish to display out of all the columns from the data.
If you do not wish to use any library for displaying table, that is also possible only that you have to replace ReactTable with your custom table implementation.
Hope this helps.
Thanks to this page: https://www.skptricks.com/2019/02/can-you-consolelog-in-jsx.html.
You can do console.log in the render function of a class component or in the return statement of a function component. In your case:
function Foo(props){
data = axios.get('http://localhost:5000/result')
return(
<>
{console.log(data)}
</>)
}
In my opinion, it's much more straight forward than the other state methods.

React Native for each item in JSON object returned by AsyncStorage create a component on screen

I have a page currently that stores data into AsyncStorage as a stringified JSON object with the keys: "title", "description" and "rating". For each JSON object stored in Async, I would like to create a new component in a specific using the component
I have got this being read and converted back into JSON objects in a different page. My issue is I would like to have this data used to populate the screen with premade component called "calendarEntryBox" for each JSON object.
Currently I am able to have one component made for each item in an array, this array has items from Async pushed to it. For testing purposes it is stored with premade entries before Async does anything. However, since this AsyncStorage is async, when I try to put data from Async and store it in this array, the map function within the screens return view has already done it.
How can I take JSON objects retrieved asynchronously, use them to create a component and put this component into a on a page?
Thanks
Here is the code I have for pushing items to an array from
AsyncStorage
const getData = async () => {
try{
var allDaysAsString = await AsyncStorage.getItem("Days");
var allDaysAsJSON = JSON.parse(allDaysAsString);
Object.keys(allDaysAsJSON).forEach(function(key) {
// console.log(Object.keys(allDaysAsJSON));
calendarEntries.push({
title: allDaysAsJSON[key].title,
description: allDaysAsJSON[key].description,
rating: allDaysAsJSON[key].rating
});
});
Here is the code I have for creating a view from each of the items in the array:
<View style={styles.calendarContainer}>
{calendarEntries.map((item, key)=>(
<CalendarEntryBox key = {key} style = {styles.smallBox} />)
)}
</View>
This works for test items already stored into the calendarEntries array:
var calendarEntries = [{rating: "bad", description: "hello I am a descrip", title: "what a title this is"},{rating: "bad", description: "hello I am a descrip", title: "what a title this is"}];
But when items are added to this array using the getData function, this is not rendered on the users view.
The codes which are provided didn't give enough info how you implemented what you wanted. here is an approach i would take for your case
in your component state i add a property named calendarEntries
state = {
calendarEntries: []
}
and change getData function to return an array containing the parsed values when resolved(its async)
and in componentDidMount i would call getData
async componentDidMount() {
const parsedValuesArray = await this.getData()
this.setState({ calendarEntries: parsedValuesArray )} // will make page re-render
}
and in your render function you use the state property:
{
this.state.calendarEntries.map((item, key)=>(
<CalendarEntryBox key = {key} style = {styles.smallBox} />)
)
}
the codes shown are simplified as much as i could just to show you an approach. hope it helps

React-admin: How to create user profile form in react-admin v3.2.*?

I've noticed that the official article on how to create user settings (profile) in React-Admin is outdated (https://marmelab.com/blog/2019/03/07/react-admin-advanced-recipes-user-profile.html).
I followed the example and tried to use a new DataProvider, but couldn't get Edit view working (it just showed blank Card component with no fields even though I've set them in a way that's described in the example).
I was searching for several days on how to implement it in a simplest/clean way, but there's a very small amount of information about it.
Does somebody know how to do it in react-admin 3.2.*?
It might be helpful for others who have the same issue.
Any help will be very appreciated! Thanks!
I had the same problem. Looking at the props passed toreact-admin's Edit, I saw the record prop was undefined. It was because the id field inside the record returned by the data provider's getOne method was different than the id prop hardcoded on the Edit component. Once that was set to match, both reading/editing works.
My working code:
// remove staticContext to avoid React 'unknown prop on DOM element' error
export const PrincipalEdit = ({ staticContext, ...props }: { staticContext: any; props: any }) => {
return (
// `id` has to match with `id` field on record returned by data provider's `getOne`
// `basePath` is used for re - direction
// but we specify no redirection since there is no list component
<Edit {...props} title="My Profile" id="my-profile" resource={b.PRINCIPAL} basePath="my-profile" redirect={false}>
<SimpleForm>
<TextInput source="firstName" validate={required()} />
</SimpleForm>
</Edit>
);
};
The issue is how the data is stored by react-admin at the moment (haven't checked how it was before). Now each data entry is saved by its id in the store object (the reasons are obvious). I think the best approach is to modify the data provider.
if (resource === 'profile') {
const { id, ...p } = params; // removes the id from the params
if (p.data)
delete p.data.id; // when updates there is data object that is passed to the backend
return dataProvider(type, resource, { ...p, id: '' }) // set the id just empty string to hack undefined as http param
.then(({ data }) => {
return Promise.resolve({
data: {
...data,
id // return the data with the initial id
}
});
});
}
This way the backend endpoint could return just the object at the main endpoint of the entity /profile. If you do not set the id prop to '' it will try to fetch /profile/undefined because the id prop was deleted from the params object. If you do not delete the id prop from the data object when updating, depending on the backend sql query (assuming you are using some kind of db) for updating a record, it may try to set or search by this id.
In the Edit component you can pass whatever id you want but something must be passed.
Additional:
If you are using NestJS as backend with the Crud package, these #Crud params may be helpful
...
params: {
id: { // the routes won't have params, very helpful for me/profile controller
primary: true,
disabled: true,
},
},
routes: {
only: ["getOneBase", "updateOneBase"],
},
...

Delete an object inside of array with firestore

I want to query the specific object by the property "id" inside the array and remove/delete the whole object. Is this possible through firestore?
My firestore structure looks like this:
For now I have function called removeCard() that takes in the docId which is the document id in firestore, and the cardId which is property "id" value inside the object.
removeCard() function:
export const removeCard = (retroId, cardId) => {
return (dispatch, getState, { getFirestore }) => {
// make async call to database
const firestore = getFirestore();
firestore.collection('retros').doc(retroId).update({
// firestore query to remove/delete this card.
}).then(() => {
dispatch({ type: 'DELETE_CARD_SUCCESS' });
}).catch(err => {
dispatch({ type: 'DELETE_CARD_ERROR' }, err);
});
}
};
But I cant figure out how to do this query or if it's even possible to do with Firestore. Any help is appreciated. Just tell me if i should provide with any more information to make my question more clear.
Actually, at the time of writing, it is not possible, with array-contains, to query for a specific property of an object stored in an array.
A possible workaround exists, which is to query for the entire object.
In your case it would be like
db.collection('retros').where("cardsAction", "array-contains", {id: "65....", content: "this is...", time: ....})
but obviously this workaround will not be useful in your case as both the time and content values may vary.
So in other words, you will probably need to change your data model.
One possible approach would be to duplicate your data (quite common in NoSQL world) and have another array with only the ids of the cardAction, like:
cardActionIds = ["65....", "5789", ....]
This way you can use array-contains and once you have found the docs corresponding to the cardActionIds you manipulate the other array (i.e. cardAction)

Resources