Hi I am trying to get todo array field from cloud firestore database in a react project. When i print out with console log I can see the empty array symbol but It throws error at the same time and does not render the page.
This is the Firestore.js file to get data from firebase.
export const userTodoList = async (email) => {
await onSnapshot(doc(db, "users", email), (doc) => {
console.log(doc.data().todos);
return doc.data().todos;
});
};
This the MainPage.js file to get the array and pass to CardList component.
const MainPage = () => {
const todoArray = userTodoList(auth.currentUser.email);
const [todos, setTodos] = useState(todoArray);
return (
<div>
<section>
<NavBar></NavBar>
<ToDoForm />
</section>
<section>
<CardList array={todos} />
</section>
</div>
);
};
This is the CardList component to render the cards with the array mapping. I am checking for if the array length is 0 or not but still get errors.
const CardList = (props) => {
const array = props.array;
if (array.length === 0) {
return (
<div className="grid_container">
<h2>Found no todos</h2>
</div>
);
}
return (
<div className="grid_container">
{array.map((todo) => (
<Card title={todo.title} />
))}
</div>
);
};
Errors.
The object.map is not a function error occurs because the map method is not implemented on objects. To iterate over an object, use the Object.keys() method to get an array of the object's keys and call the map()method on the array of keys.
I.e You can use Object.keys() or Object.entries() and then use map
For example:
array.keys(obj).map(key => {
//other statements
});
For more details you can check this article
Related
Hi I am trying to map an array of an api to display paragraphs for every index within the array. However I keep getting an error :
**> TypeError: undefined is not an object (evaluating
'post.game_indices.version.name')**
But when I console log post and use my buttons below it displays what I want and not undefined. So why is it undefined when I want to map the paragraphs?
'''
import React, {useEffect,useState} from 'react'
import instance from './axios'
const Home = () => {
const [post, setPost] = useState(null);
const [error,setError] = useState(null);
const [showTypes,setShowTypes]=useState(false);
const [showAbilities,setShowAbilities]=useState(false);
useEffect(() => {
instance.get("bulbasaur/").then((response) => {
setPost(response.data);
}).catch(error => {
setError(error);
})
},[]);
console.log(post);
if (error) return `Error: ${error.message}`;
if (!post) return 'no post!'
return (
<>
<h1>{post.name}</h1>
<button onClick={()=>setShowTypes(!showTypes)}>types</button>
{showTypes? <p>{(post.types[0].type.name)}</p>:null}
{showTypes? <p>{(post.types[1].type.name)}</p>:null}
<button onClick={()=>setShowAbilities(!showAbilities)}>abilities</button>
{showAbilities? <p>{(post.abilities[0].ability.name)}</p>:null}
{showAbilities? <p>{(post.abilities[1].ability.name)}</p>:null}
{/* <button onClick={gameVersion}>game versions</button> */}
{post.game_indices.version.name.map(name => <p key={post.game_indices.version.name}>{name}</p>)}
</>
)
}
export default Home;
'''
ok, I recreated your app and found a problem - you should map on array post.game_indices and then display index.version.name
{post.game_indices.map((index, idx) => <p key={idx}>{index.version.name}</p>)}
The error comes from the fact that you version is not an object in game_indices (it is undefined, which means maybe game_indices is an empty object or it does not contain the version key). Can you please check your post object again ?
I wanted to create a e-commerce web application using react-bootstrap. I want the page to show different item based on category so if the URL is product/men'sclothing i want to filter my array and show only the product that have same category which is men's clothing (my path: product/:category). I already tried to filter my array using .filter method but it didn't work, it still show all product from various category, How can I fix it ?
Categorized product page:
const ProductList = () => {
const { category } = useParams()
const[productList, setProductList]= useState();
useEffect(() =>{
axios.get(`https://fakestoreapi.com/products`).then(res => {
const products = res.data;
setProductList(products);
var filteredCategory =
productList.filter((productList) =>productList.category === {category})
})
}, []);
console.log(productList)
return (
<>
<Row>
<h1> This is {category} paged</h1>
{productList && productList.map(product =>{
const {id, title, price, category,description,image} = product;
return(
<Col lg={3} className="d-flex">
<Card key={id} className="flex-fill productlist">
<Card.Img variant="top" src={image} />
<Card.Body>
<Card.Title>{title}</Card.Title>
<Card.Text>{category}</Card.Text>
<Card.Text>
Current Price: {price}
</Card.Text>
<Button variant="primary">Add to cart</Button>
</Card.Body>
</Card>
</Col>
)
})}
</Row>
</>
)
}
export default ProductList
In the filter function that you have used, try writing it as
productList.filter((product) => product.category === category)
When you write it as {category}, a object is created with key category and the value as the actual value. For example if value of category is shoes, it will create a object, { category: "shoes" }.
You also need to add category in useEffect dependency, to re-fetch products every time category is updated.
First, add a dependency to your UseEffect then remove the bracket inside the filter.
useEffect(() => {
async function getByCategory(){
const req = await fetch(URL):
const res = await req.json();
const filter = res.filter((item) => item.category === category);
setProductList(filter);
}
// check if params exits
if(category){
getByCategory();
}
}, [category]);
Try getting rid of the {} around the category variable in the filter function. The filter function is not inside the return statement and thus plain js (not jsx).
Also, you're never using the array containing the filtered products. I'd suggest to filter the products you get from axios, take the filtered products and put THEM into state with setProductList.
Was not able to test this since I'm on mobile, but give it a try.
Remove the curly braces when comparing the element.
__YOUR CODE
productList.filter((productList) =>productList.category === {category})
__NEW
productList.filter((productList) =>productList.category === category)
You are still listing all products because in your code you are looping through the productList state instead of the new value which come from the filtered data.
{productList && productList.map(product =>{
// this is the way you have defined your map code
}) }
It should be like this
const ProductList = () => {
const { category } = useParams()
const[productList, setProductList]= useState();
useEffect(() =>{
axios.get(`https://fakestoreapi.com/products`).then(res => {
const products = res.data;
setProductList(products);
})
}, []);
let filteredProducts = null;
if(category) {
filteredProducts = productList.filter((productList) => productList.category === category);
} else {
filteredProducts = products;
}
return (
<>
<Row>
<h1> This is {category} paged</h1>
{filteredProducts && filteredProducts.map(product =>{
// some code
})}
</Row>
</>
)
}
export default ProductList
As you can see I define a variable filter Products which contains products related to the category get from the url when It's present otherwise it will use the entire list of products
I'm learning to use firebase and react. I have shared my firestore collection image. and my code for fetching the array from my document is given below.
This code is fetching the data from my firestore database and then storing the result in my watchlistMovies react state. when i try to log the react state or even data.data() it gives the desired result but when i try to map over the array or do something similar like logging watchlistMovies.myList[0].media_type it hits me with an error. i tried my best trying different things making it work but it breaks a thing or two in process.
I hope someone here will help me. Thank you in advance! : )
updated the code
const Watchlist = () => {
const [watchlistMovies, setwatchlistMovies] = useState([]);
const {currentUser} = useAuth()
const usersCollectionRef = collection(db,"users")
const docRef = doc(db,"users",currentUser.uid)
useEffect(() => {
const getWatchListMovies = async () => {
const data = await getDoc(docRef)
if (data.exists()) {
console.log(data.data());
setwatchlistMovies([...watchlistMovies ,data.data().myList])
} else {
console.log("empty");
}
}
getWatchListMovies();
}, [])
console.log(watchlistMovies);
// console.log(watchlistMovies.myList[0]);
return (
<div className="content-page-area">
<h1 className="trending-text"> My Watchlist </h1>
<Container className="watchlist-container">
<hr/>
{watchlistMovies.map(
(item) => (
<ListContent
item_poster={item.poster_url}
item_title={item.media_title}
item_year={item.release_year}
item_rating={item.media_rating}
item_type={item.media_type}
item_id={item.media_id}
/>
)
)}
</Container>
<br/>
<br/>
<br/>
</div>
)
}
export default Watchlist
I am learning React and I was trying to develop a code to fetch random 10 numbers with corresponding interesting facts from an api "Numbers API".
the issue I am facing is that, when I run the code, an error appears
"Error: Objects are not valid as a React child (found: [object Promise]). If you meant to render a collection of children, use an array instead"
below I have attached the code:
const originalArray = new Array(10).fill(0);
const mappedArray = originalArray.map((n, index) =>
Math.floor(Math.random() * 100)
);
console.log("mappedArray", mappedArray);
console.log("joined array string", mappedArray.join(","));
async function callApi(numbers) {
const fetchResponse = await fetch(`http://numbersapi.com/${numbers}/math`);
if (fetchResponse.ok) {
const data = await fetchResponse.json();
console.log("all good, heres the response", data);
} else console.log("There was a problem");
}
const Number = async() => {
const [add, setAdd] = React.useState([]); // <-- valid initial empty state
React.useEffect(() => {
callApi(mappedArray).
then(values => setAdd(values));
}, []); // <-- invoke on component mount
<div className="container">
{add.map((newlist, i) => (
<div className="item" key={i}>
{newlist}
</div>
))}
</div>
);
export default Number;
Can someone help me fix this issue? Thank you
You need to maintain state. React is very particular with it's lifecycle. So here I have a data state, and a function - setData - that updates it.
For React function components we use useEffect - essentially the old componentDidMount from class components - to load data when the component first mounts. We can then set the state with that data which we now know is an array of objects.
Once the state updates the component gets re-rendered. You can then iterate over the Object.entries using the key for the key, and the value as the text source.
function Number() {
// Initialise the state with an empty array
const [data, setData] = useState([]);
// Separate out the function that creates the number array
function createNumbers() {
const originalArray = new Array(10).fill(0);
return originalArray.map(() => Math.floor(Math.random() * 100)).join('');
}
useEffect(() => {
async function getNumbers() {
try {
const res = await fetch(`http://numbersapi.com/${createNumbers()}/math`);
const data = await res.json();
// Set the new component state using the data
setData(data);
} catch (err) {
console.log(err);
}
}
getNumbers();
}, []);
return (
<div className="container">
{Object.entries(data).map(([key, value]) => (
<div className="item" key={key}>
{value}
</div>
))}
</div>
);
};
export default Number;
callApi is async so it implicitly returns a Promise, which you save into add and attempt to render. This is the object that React is complaining about.
You will want to invoke this function within the React component lifecycle and save the result into state to be rendered.
const Number = () => {
const [add, setAdd] = React.useState([]); // <-- valid initial empty state
React.useEffect(() => {
callApi(mappedArray).
then(values => setAdd(values));
}, []); // <-- invoke on component mount
return (
<div className="container">
{add.map((newlist, i) => (
<div className="item" key={i}>
{newlist}
</div>
))}
</div>
);
};
When you send a comma separated list of numbers to this numbersapi it returns a JSON object that isn't renderable as an array.
The response format will always be a JSON map from numbers to facts,
of at most 100 numbers.
Example response to http://numbersapi.com/53,65,70/math
{
"53": "53 is the 16thprime number.",
"65": "65 is the 23rdsemiprime and the 3rd of the form (5.q)it is an octagonal number.",
"70": "70 is the smallest weird number."
}
It seems you want to either save the values into state:
React.useEffect(() => {
callApi(mappedArray).
then(result => setAdd(Object.values(result)));
}, []);
Or you can get the array of values when rendering:
return (
<div className="container">
{Object.values(add).map((newlist, i) => (
<div className="item" key={i}>
{newlist}
</div>
))}
</div>
);
If you want both the key and the value, then use Object.entries to get an array of array of key value pairs.
Example:
[
['53', '53 is the 16thprime number.'],
['65', '65 is the 23rdsemiprime and the 3rd of the form (5.q)it is an octagonal number.'],
['70', '70 is the smallest weird number.'],
]
return (
<div className="container">
{Object.entries(add).map(([key, value], i) => (
<div className="item" key={key}>
{key}: {value}
</div>
))}
</div>
);
That's because your add function is still return a Promise since it an async function
You will still need to await for add to finished, then use it:
Something like this:
const Number = async () => {
const data = await add;
<div className="container">
{data.map((newlist, i) => (
<div className="item" key={i}>
{newlist}
</div>
))}
</div>;
};
I'm attempting to map over data I received from an API call. Getting shallow endpoints works fine, but anything nested gives me an error.
The goal is to get all of the opening themes and display them in a 'ul'.
The exact error "TypeError: anime.opening_themes is undefined"
Repo to the project
Heres the endpoints.
Heres my component.
const AnimeDetails = (props) => {
const API = 'https://api.jikan.moe/v3/anime'
const initialState = {
anime: []
}
const [anime, setAnime] = useState(initialState)
useEffect(() => {
const getAnime = async () => {
const response = await fetch(`${API}/${props.match.params.animeId}`)
const data = await response.json()
console.log(data);
setAnime(data) // set initial state to hold data from our API call
}
getAnime()
}, []) // [] prevents useEffect from running in an infinite loop
return (
<AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>
)
}
Your initial state is an empty array, not an empty object:
const initialState = {
anime: []
}
When your component mounts, there is no data yet, so you're attempting to render [].opening_themes.map, and obviously there is no opening_themes property on an empty array.
Set your initial state to an empty object instead:
const initialState = {}
And you will probably want to test that you have data before attempting to render it:
return anime.mal_id && (
<AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>
)
This will prevent your component from rendering anything until your anime state contains a mal_id property.
The first time you render your component, the state anime is equal to {anime: []}, which has no property called opening_themes.
you should try like this
first, remove initialState code and use direct code like following
if the response is in the form of an array
const [anime, setAnime] = useState([])
if the response is in the form of an object
const [anime, setAnime] = useState({})
otherwise, null will work with any response
const [anime, setAnime] = useState(null)
return code like this
return (
<> {anime && <AnimeDetailsWrapper>
<Title>{anime.title}</Title>
<Details>
{anime.opening_themes
.map((song, index) => (
<li key={index}>{song}</li>
))}
</Details>
</AnimeDetailsWrapper>}</>
)