Trying to render or merge multiple Object.keys - reactjs

I am currently receiving data from Spotify via Search API.
But, I don't know how to handle multiple objects.
Screenshot
I am currently, able to: response.data.albums.items
and then map((item) => { });
Is there a way to render all of them, Albums, Artists, and Tracks?
Render to an <ul><li></li></ul>.

You can use Object.keys.
const keyArray = Object.keys(response.data);
const mergedArray = keyArray.map((key) => {
console.log(response.data[key]); // Albums, Artists, Tracks
return response.data[key].items; // I am assuming Albums,Artists and Tracks have items key.
});
console.log(mergedArray);

Related

Render items from an array of objects, using another array with ids: ts error and best practice

I'm doing a library project (see the link for more explaination) with react, redux-toolkit and typescript.
I'm doing an addToWishlist funcionality: the user simply click on the star, the code dispatches the action which patch the database and the state is updated. There is another action to remove the book from the list.
In <Wishlist /> component I want of course render the titles and other data of each book. So I useAppSelector to retrieve wishlist and the entire list of books, and with help of filter and map I would get the corresponding books, but I'm not able to render them, because it gives me a ts error: index.d.ts(1373, 9): The expected type comes from property 'children' which is declared here on type 'DetailedHTMLProps<HTMLAttributes<HTMLUListElement>, HTMLUListElement>'
Important note about the structure
wishlist is an array of strings, which are the ids of the books.
The entire list of books instead, is an array of objects: every object is a book, with title, author, id etc.
My questions:
What is going wrong here?
Basically, is it better to save in the wishlist array the ids (like I've done) and so I've to retrieve the data of books in my <Wishlist /> component with filter and map, or is it better to save the entire book (or at least, the infos that I need), so that is more easy to have the data?
Which is the best practice, to render the list, in the first case, where I have to retrieve data using filter and map?
Here is the attempt:
const Wishlist = () => {
const dispatch = useAppDispatch();
const wishlist = useAppSelector(state => state.user.userInfo.wishlist);
const list = useAppSelector(state => state.catalogue.list);
const renderBooks = () => {
return wishlist.map(wishId => {
list.filter(book => {
if (book.id === wishId) {
return (
<li>{book.title}</li>
)
}
})
})
}
return (
<ul>
{renderBooks()}
</ul>
)
};
export default Wishlist;
Edited - maybe the problem is not the concatenation of methods, but something in the function calling?
Another more little example, with another solution, but same problem
interface fakeObj {
id: string;
name: string;
}
const fakeIds = ['5', '12', '878'] // this is my 'wishlist', containing only ids
const fakeList: fakeObj[] = [ // this is my 'list', containing objects, which were single books
{ id: '334', name: 'Tony' },
{ id: '5', name: 'Milly' },
{ id: '12', name: 'Jack' },
{ id: '7', name: 'Stew' },
{ id: '878', name: 'Mark' },
{ id: '87998', name: 'Greg' }
]
const renderArray = () => {
const arr: fakeObj[] = [];
fakeIds.forEach(id => {
fakeList.filter(obj => {
if (obj.id === id) {
arr.push(obj)
}
})
})
return arr; // the returned array is what I want: an array continaing the objects that i need
}
return (
<ul>
{/* The problem actually is here! */}
{renderArray()}
</ul>
)
You're not using .filter() or .map() correctly, which is what's causing the errors.
The comment above that you rejected was the correct solution to this problem.
The added edited example is even more confusing, because you're no longer trying to return renderable elements, as the return is an array of objects, which can't be rendering in a UL.
So, I'm going back to the initial example.
Problem #1:
You're trying to return a JSX element within the callback passed to .filter(). This is not how .filter() works. You're supposed to pass a callback to .filter() that will return TRUE or FALSE dependent on whether or not you want the element to be filtered in or filtered out. E.g. listOfCounts.filter( count => count > 5); would return a modified array of all elements in listOfCounts that are greater than 5.
Problem #2:
You're currently calling the .map() first, which is wrong, but then also calling the .filter() WITHIN the .map() instead of chaining the map AFTER the filter. Because right now, your .map() call is not even attempting to return a list of JSX elements representing the book titles you want to render. These calls are meant to be chained opposite of how you have it.
An example of how to use .map() that's very simple:
listOfNames.map(name => <div>{name}</div>);
A simple example combining .map() with .filter():
listOfNames.filter(name => name[0] == 'J').map(name => <div>{name}</div>);
which filters out all names that don't start with an uppercase J.
The solution above that Mr. Rafique provided you is correct / very close to correct depending on exact implementation, so I'd suggest you combine this added info with his solution.

How to get an object from firebase in react native

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

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

GraphQL Automatic refetch on empty responses

I want to randomize movies from theMovieDB API. First I send a request to access the ID of the latest entry:
const { loading: loadingLatest, error: errorLatest, data: latestData, refetch: refetchLatest } = useQuery(
LATEST_MOVIE_QUERY
);
Then I want to fetch data from a randomly selected ID between 1 and the number of the latest id. Using a variable dependant on the first query seems to break the app, so for now I'm just using the same movie every time upon mounting the component:
const [
movieState,
setMovieState
] = useState(120);
const { loading, error, data, refetch } = useQuery(ONE_MOVIE_BY_ID_QUERY, {
variables : { movieId: movieState },
skip : !latestData
});
I want to press a button to fetch a new random movie, but the problem is that many of the IDs in the API lead to deleted entries and then I get an error back. I want to keep refetching until I get a good response back but I have no idea to implement it. Right now my randomize function just looks like this:
const randomizeClick = () => {
let mostRecentID = latestData.latestMovie.id;
setMovieState(Math.floor(Math.random() * mostRecentID));
};
I'd be grateful if someone can help me how to implement this.
I think what you needs is the "useLazyQuery" functionality of Apollo. You can find more information about it here: https://www.apollographql.com/docs/react/data/queries/#executing-queries-manually
With useLazyQuery you can change your variables easily and this is meant to be fired after a certain event (click or something similar). The useQuery functionality will be loaded when the component is mounted.

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