Getting map data using React from firestore - reactjs

I am having trouble trying to figure out how to get map data from Firestore in reactjs. My code keeps erroring saying "Objects are not valid as a React". Can someone point me to an example or show me one with my database below?
import React, { useState, useEffect } from "react";
import { firestore } from "../../../FireBase/FireBase";
import CartItem from "./CartItem";
const CartPage = (props) => {
const [cart, setCart] = useState(null);
useEffect(() => {
const fetchCart = async () => {
const doc = await firestore
.collection("Users")
.doc("CfL5uszL3CTE1nIQTgDrKK5q4OV2")
.get();
const data = doc.data();
console.log("data " + data);
if (!data) {
// document didn't exist
console.log("hit null");
setCart(null)
} else {
console.log("hit");
setCart(data.cart);
}
console.log("cart " + cart);
}
fetchCart();
}, []);
if (!cart) {
// You can render a placeholder if you like during the load, or just return null to render nothing.
return null;
}
return (
<div className="cartpage">
<h1>cart</h1>
<div className="cart">
{cart.map(cartItem => (
<div key={cartItem.id}>{cartItem.name}</div>
))}
</div>
</div>
);
};
export default CartPage;

The error your getting is because you're returning a promise from your component (You've made it an async function, and async functions return promises). Promises and other arbitrary objects cannot be returned from rendering in react. You need to have a state variable for holding your data. On the first render, you'll have no data, and then you'll use a useEffect to fetch the data and update the state
Additionally, you have some mistakes with how you're trying to get the data and access it. You're calling .get("Cf...V2"), but .get doesn't take a parameter. If you want to specify which document to get, you use the .doc() function for that. .get() will then return a promise, so you need to await that before trying to access any properties on it. The data you get will be an object with all the properties on the right hand side of your screenshot, and you will need to pluck the cart property out of that.
In short, i recommend something like the following:
const CartPage = (props) => {
const [cart, setCart] = useState(null);
useEffect(() => {
const fetchCart = async () => {
const doc = await firestore
.collection("Users")
.doc("CfL5uszL3CTE1nIQTgDrKK5q4OV2")
.get();
const data = doc.data();
if (!data) {
// document didn't exist
setCart(null)
} else {
setCart(data.cart);
}
}
fetchCart();
}, []);
if (!cart) {
// You can render a placeholder if you like during the load, or just return null to render nothing.
return null;
}
return (
<div className="cartpage">
<h1>cart</h1>
<div className="cart">
{cart.map(cartItem => (
<div key={cartItem.id}>{cartItem.name}</div>
))}
</div>
</div>
);
};

I don't think so that you can create async component in this way. What you return in your component should be simple JSX code. If you want to do something asynchronously inside component you should wrap this inside useEffect hook.
const CartPage = (props) => {
const [ cart, setCart ] = useState(null)
useEffect(() => {
const inner = async () => {
const ref = await firestore
.collection("Users")
.get("CfL5uszL3CTE1nIQTgDrKK5q4OV2").cart;
setCart(
ref.map((item) => ({
id: item.id,
name: item.name
}))
);
};
inner();
}, []);
return (
<div className="cartpage">
<h1>cart</h1>
<div className="cart"></div>
</div>
);
};

Related

React Typescript fetch: useState UseEffect pokeapi

I am trying to learn how to fetch data and display in a table/list/graph in React.
I extracted the fetch to a component and while i can get the list to appear i think this is wrong - Why and how to fix?
// getData.tsx
import React, { useState, useEffect } from 'react';
let myArray: string[] = [];
export default function GetData() {
const [info, setData] = useState([]);
useEffect(() => {
getMyData();
}, []);
const getMyData = async () => {
const response = await fetch('https://pokeapi.co/api/v2/type')
const data =await response.json();
//console.log(data.results)
for (var i = 0; i < data.results.length; i++) {
myArray.push(data.results[i].name)
setData(data.results[i].name)
}
console.log(info)
}
return (
<div>
<h1>get Data</h1>
{myArray.map((value,index) => {
return <li key={index}>{value}</li>;
})}
</div>
)
}
Also same issue but do not understand why the names and Array don't both work?
export default function GetData(){
const names: string[] = ["whale", "squid", "turtle", "coral", "starfish"];
const theArray: string[] = [];
const getData = async () => {
const response = await fetch('https://pokeapi.co/api/v2/type');
const data = await response.json()
//for (var i = 0; i < data.results.length; i++) {
for (var i = 0; i < 5; i++) {
theArray.push(data.results[i].name)
}
console.log(theArray)
}
console.log(names)
console.log(theArray)
getData()
return (
<div>
<ul>{names.map(name => <li key={name}> {name} </li>)}</ul>
<h1>get Data</h1>
<ul>{theArray.map(name => <li key={name}> {name} </li>)}</ul>
</div>
)
}
You aren't using the state data... The issue is that.
The correct way to do this:
const [data, setData] = useState([])
useEffect(() => {
fetch('https://pokeapi.co/api/v2/type')
.then(res => res.json())
.then(setData)
},[])
return <div>
<ul>
{data.map((name) => <li key={name}>{name}</li>}
</ul>
</div>
The problem is getData is declared as async function. That means it's returning Promise that you can await on and get it's result. But you never do that. You're using it without await essentially not waiting for it finish and discarding its result.
To get the result of async function you should await on it. In your second component you'll have to write this:
...
console.log(names)
console.log(theArray)
await getData() // add 'await' to well... wait for the result of the getData execution
return (
...
But you can await only inside async function aswell. As far as I'm concerned you're not able to use async components now (react#16-17). So the second component is not going to work as intended. At least untill react is able to support async components.
Though there are some issues even with your first component.
let myArray: string[] = [];
Declared in the module scope it will be shared (and not reseted) between all instances of your component. That may (and will) lead to very unexpected results.
Also it's quite unusuall you don't get linting errors using getMyData before declaring it. But I suppose that's just an artefact of copy-pasting code to SO.
Another problem is you're using setData inside your component no to set the contents of myArray but to trigger rerender. That's quite brittle behavior. You should directly set new state and react will trigger next render and will use that updated state.
To work properly your first component should be written as:
import React, { useState, useEffect } from 'react'
export default function GetData() {
const [myArray, setMyArray] = useState([])
const getMyData = async () => {
const response = await fetch('https://pokeapi.co/api/v2/type')
const data = await response.json()
const names = data.results.map((r) => r.name) // extracting 'name' prop into array of names
setMyArray(names)
}
useEffect(() => {
getMyData();
}, []);
return (
<div>
<h1>get Data</h1>
{myArray.map((value,index) => (
<li key={`${index}-${value}`}>{value}</li>
))}
</div>
)
}

I receive "TypeError: items is undefined" when trying to map items from a JSON

I am currently trying to setup a React web app using React hooks. I try to pull the items from the JSON with Map but I receive this error.
TypeError: items is undefined
Shop.js
import React, {useState, useEffect} from 'react';
import './App.css';
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const data = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const items = await data.json();
console.log(items.items);
setItems(items.items);
};
return (
<div>
{items.map(item => (
<h1>{item.map}</h1>
))}
</div>
);
}
export default Shop;
I'm not an expert on React, but I'm pretty sure its because you get to the return statement before you define items.
That function is async, so while it takes its turn running each line, the program itself will move on, thus getting to the return with "items" in it before items is actually defined.
What might fix it is doing an if/else that checks if items is defined, then returns either blank html or the html with items. React dynamically updates so that should then return the correct html once items is loaded.
This should solve your question. I have checked the response from the GET request you provided and you have used the incorrect data structuring when pulling fields out. Try the code below.
import React, {useState, useEffect} from 'react';
import './App.css';
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const response = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const deserialisedResponse = await response.json();
setItems(deserialisedResponse.data);
};
return (
<div>
{items.map((item, index) => (
<h1 key={index}>{item.map}</h1>
))}
</div>
);
}
export default Shop;
I've tried on my end and confirmed it works.
Let me show the codes.
function Shop() {
useEffect(() => {
fetchItems();
}, []);
const [items, setItems] = useState([]);
const fetchItems = async () => {
const response = await fetch('https://fortnite-api.theapinetwork.com/upcoming/get');
const deserialisedResponse = await response.json();
console.log("result: ", deserialisedResponse)
setItems(deserialisedResponse.data);
};
return (
<div>
{items.map((item, idx) => (
<h1 key={idx}>{item.item.name}</h1>
))}
</div>
)
}
Please have a check and let me know if it works or not.

React won't return more than one document from firebase

I am new to using React with Firebase and I am struggling to return the data that I have in firebase. I have a collection called "users" and multiple documents inside with auto-generated IDs. I also have 3 fields in each document, fullname, email and id. This is the code I am using to fetch the documents:
function App() {
const db = firebase.firestore();
const [users, setUsers] = useState([])
const fetchUsers = async () => {
const response = db.collection('users');
const data = await response.get();
data.docs.forEach(item => {
setUsers([...users, item.data()])
})
}
useEffect(() => {
fetchUsers();
}, [])
return (
<div>
{
users && users.map(user => {
return (
<div key={user.id}>
<div>
<h4>{user.fullname}</h4>
<p>{user.email}</p>
</div>
</div>
)
})
}
</div>
);
}
In the console, it is returning all of the documents in individual arrays but on the webpage, it is only returning the last document. Is there a way to return all of the documents? Any help would be appreciated, thank you.
On your fetchUsers function you need to pass in a function with the previous state.
const fetchUsers = async () => {
const response = db.collection('users');
const data = await response.get();
data.docs.forEach(item => {
setUsers((prevState)=>{return ({[...prevState, item.data()]})})
})
}

Cannot read property 'map' of undefined on useState value

I'm new to react, I'm getting this error constantly and after google some I can't find the reason why the useState value can't be read as array :( ... this the error I'm getting: 'TypeError: team.map is not a function'
import React, { useEffect, useState } from "react";
const SportTeams = () => {
const [team, setTeam] = useState([]);
useEffect(() => {
const getSports = async () => {
const response = await fetch("https://www.thesportsdb.com/api/v1/json/1/all_sports.php");
const data = await response.json();
setTeam(data);
console.log(data);
}
getSports();
}, []);
return (
<div className="myClass">
<ul>
{team.map((sport, index) => {
return <li key={`${sport.strSport}-${index}`}>{sport.strSport}</li>
})}
</ul>
</div>
);
};
export default SportTeams;
Just update setTeam like following, your error will be resolved.
setTeam(data.sports);
It is because you are setting the team state with the data without checking if its undefined. If the data is undefined your state team become undefined as well. So make sure to check the data.
import React, { useEffect, useState } from "react";
const SportTeams = () => {
const [team, setTeam] = useState([]);
useEffect(() => {
const getSports = async () => {
const response = await fetch("https://www.thesportsdb.com/api/v1/json/1/all_sports.php");
if (response) {
const data = await response.json();
if (data) {
setTeam(data);
}
}
console.log(data);
}
getSports();
}, []);
return (
<div className="myClass">
<ul>
{team.map((sport, index) => {
return <li key={`${sport.strSport}-${index}`}>{sport.strSport}</li>
})}
</ul>
</div>
);
};
export default SportTeams;
There might also be the chance that your response is not what you expected and the actual data might be inside your response. In that case you need check what your response first then proceed to set the data.
As I said in my comment. the value you are setting to teams isn't an array.
const data = await response.json();
setTeam(data.sports);

How to fetch data from restAPI and from localStorage at a time?

What I am doing is to load data from my local storage which I have fetched from https://jsonplaceholder.typicode.com/users. I am trying to create a react application in which I have added multiple users as a friend just like facebook. my friend list is in the file called UserInfo.js which code I have given bellow. then I tried to show friends corresponding to their id by comparing with the id which I tried to find from api call so that this can show me the matching users.
You can find my project here: https://codesandbox.io/s/elastic-dream-mp3is?file=/src/App.js
import React, { useState } from "react";
import "./UserInfo.css";
import { useEffect } from "react";
import fakedata from "../../fakedata";
import { getDatabaseCart } from "../../utilities/databaseManager";
const UserInfo = () => {
// // using fakedata
// const [users, setUsers] = useState(fakedata);
// // declaring state while calling api
const [users, setUsers] = useState([]);
// declaring state while loading data from local storage
const [friends, setFriends] = useState(users);
// to load data from Api call
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
// to load data from local storage
useEffect(() => {
const savedFriends = getDatabaseCart();
const friendsId = Object.keys(savedFriends);
const countFriends = friendsId.map((key) => {
const friend = friends.find((fd) => fd.id == key);
// console.log(friend);
return friend;
});
setFriends(countFriends);
}, [friends]);
// console.log(friends);
// console.log(users);
return (
<div className="userInfo-container">
<div className="userInfo">
{friends.map((friend) => (
<div>
<h4>Name: {friend.name}</h4>
</div>
))}
</div>
</div>
);
};
export default UserInfo;
I have created fakedata collecting data from jsonplaceholder and tested according to above method and it worked perfectly. but when I tried to load data API call, I got the following error:
the first error indicates that it can not read property of name which I tried to return from local Storage after matching the id with api call.
2.second error denotes which I can't understand in my case. I have tried abortCall to handle this error. the error gone but my problem still exits. what can I do now????
With hooks:
import React, { useState } from "react";
//import "./UserInfo.css";
import { useEffect } from "react";
import fakedata from "../../fakedata";
import { getDatabaseCart } from "../../utilities/databaseManager";
const UserInfo = () => {
// // using fakedata
// const [users, setUsers] = useState(fakedata);
// // declaring state while calling api
const [users, setUsers] = useState([]);
// declaring state while loading data from local storage
const [friends, setFriends] = useState([]);
// to load data from Api call
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => setUsers(data));
}, []);
// to load data from local storage
useEffect(() => {
const savedFriends = getDatabaseCart();
const friendsId = Object.keys(savedFriends);
setFriends(friendsId);
}, []);
// console.log(friends);
// console.log(users);
return (
<div className="userInfo-container">
<div className="userInfo">
{users.length > 0 && friends.map((friendId) => {
const friend = users.find(user => user.id === parseInt(friendId));
return (
<div>
<h4>Name: {friend && friend.name}</h4>
</div>
)})}
</div>
</div>
);
};
export default UserInfo;
In the render, Friends return [{0:undefined}]. You are not setting friends correctly.
I make a Class version of your UserInfo component. It works like this.
import React from "react";
import { getDatabaseCart } from "../../utilities/databaseManager";
class UserInfo extends React.Component {
state = {
users: [],
friends: []
}
componentDidMount = () => {
this.getDataFromServer();
this.getDateFromLocalStorage();
}
getDataFromServer = () => {
fetch("https://jsonplaceholder.typicode.com/users")
.then((res) => res.json())
.then((data) => this.setState({users:data}));
}
getDateFromLocalStorage = () => {
const savedFriends = getDatabaseCart();
const friendsId = Object.keys(savedFriends);
//console.log(friendsId)
this.setState({friends:friendsId})
}
render() {
const {users, friends} = this.state;
// console.log('users',users, friends);
// console.log('friends', friends);
return (
<div className="userInfo-container">
<div className="userInfo">
{users.length > 0 && friends.map((friendId) => {
const friend = users.find(user => user.id === parseInt(friendId));
return (
<div>
<h4>Name: {friend && friend.name}</h4>
</div>
)})}
</div>
</div>
);
}
}
export default UserInfo;
Note you have a promise for the 'fetch' so at first render and until users are fetched user = [] empty array. To avoid errors it's good to check the length of users before try to map friends.
You can remove the friend check here because if friends array is empty, there is nothing to map.
return (
<div>
<h4>Name: {friend.name}</h4>
</div>
)
Now, it should be a list with ul and li.

Resources