Can you help me figure out why I am not able to map this array. Here below are the error and the codes I am running:
TypeError: posts.map is not a function
and here is my codes causing the above error:
import React from 'react';
import {useEffect, useState} from 'react';
import { Container, Row, Col } from 'bootstrap-4-react';
export default function Post() {
//posts array to be mapped
const [posts, setPosts] = useState([{
title: "",
postContent: ""
}]);
useEffect(() => {
//fetches a GET route
fetch(`${process.env.REACT_APP_SERVER_URL}/posts/:id`).then(res => {
if (res.ok) {
return res.json()
}
}).then(jsonRes => setPosts(jsonRes));
})
return (
<div>
<h1>Hello</h1>
//cant seem to be able to map this array
{posts.map(post => {
<>
<h1>{post.title}</h1>
<p>{post.postContent}</p>
</>
})}
</div>
)}
You need to wrap the mapped returned code block within parenthesis ()
and not in curly brackets {} in order to return the html correctly
//...
return (
<div>
<h1>Hello</h1>
{posts.map(post => (
<>
<h1>{post.title}</h1>
<p>{post.postContent}</p>
</>
))}
</div>
)
}
Edit:
Also, I suggest adding an empty dependency array as the second argument for your useEffect(() => { //your code }, [])
This will make it so your component doesn't re-render sporadically and end up fetching your data a ridiculous amount of times.
This is maybe because the response is not an array. Try to console.log the response. You can also change its type by using Array.isArray(jsonRes). The second problem is you are not returning the individual element inside the map function.
{posts.map((post) => (
<>
<h1>{post.title}</h1>
<p>{post.postContent}</p>
</>
))}
Your useEffect also don't have any dependencies, this will result in fetching data on every render. So you must use an empty array as the second argument inside useEffect to tell it to execute only once.
useEffect(() => {
//fetches a GET route
fetch(`${process.env.REACT_APP_SERVER_URL}/posts/:id`).then(res => {
if (res.ok) {
return res.json()
}
}).then(jsonRes => setPosts(jsonRes));
}, [])
Related
I am trying to write a table that should be writing out properties of an object in React. For some unknown reason I cannot get it to produce any output.
const renderItem = (item) => {
return (
<div>
{Object.entries(item).map(([key, value]) =>
{console.log(value)}
(
*variations in next segment
)
</div>
*in this line I tried:
{value} // with instead of wrapping elements as shown above
{value}
Test // wrapped in elements
The console.log(value) returns all the values it should so I know I supply the data correctly and it is iterating through the array as it should but somehow I cannot get it to produce any output inside of ().
I tried playing with many different variations, thought the problem might be in css but that is also not the case. I tried outputing data also simply in or , I tried moving the function to a whole new file, but nothing.
EDIT. Here is more info:
My main App.js:
import './App.css';
import HierarchyTable from './components/HierarchyTable.js';
import exampleData from './data/example.json';
function App() {
return (
<div className="App">
<HierarchyTable datafed={exampleData} ></HierarchyTable>
</div>
);
}
export default App;
My HierarchyTable.js
import React, { useState } from 'react';
import '../App.css';
export default function HierarchyTable({datafed}) {
const [data, setData] = useState(datafed)
const renderTable = (item) => {
return(
Object.entries(item).map(([key, value]) => {
if (key !== "children"){
renderItem(value)
}
else {
renderTable(value)
}
}
)
)
};
function renderItem (item) {
return Object.entries(item).map(([key, value]) => {
return (<tr><td key={key}>{value}</td></tr>)
}
)}
return (
<div>
<table>
<thead>
<tr>
{Object.entries(data[0].data).map(([key, value]) => (
<th
// className = "HeaderItem"
>{key}</th>
)
)}
</tr>
</thead>
<tbody>
{data.map((item) => renderTable(item))}
</tbody>
<tfoot>
</tfoot>
</table>
</div>
);
}
I tried doing it in many different variatons. The renderItem function gets the data I need as I can console.log it but it does not output the html elements as it should. It breaks my spirit.
You need to properly return something from the map function:
const renderItem = (item) => {
return (
<div>
{Object.entries(item).map(([key, value]) => {
console.log(value)
return value
//or return (<p>{value}</p>)
})}
</div>
)
You most likely confused the shorthand map function that looks like data.map(value => (value))
If you use brackets {}, you need to specifically return something with "return". If you use (value) only, it is short from {return value}
I am super new to React and have just started my journey with it I have another page where I use the same function of handleIndexEmployees and the same const variables with useState and useEffect which load just fine. Right now with the code I have the only thing that shows up is
<h1>Employees</h1>
import { useState, useEffect } from "react";
import axios from "axios";
import { EmployeesAxios } from "./EmployeesAxios";
export function EmployeesIndex() {
const [employees, setEmployees] = useState([]);
const handleIndexEmployees = () => {
console.log("Wait I'm getting Da Minions");
axios.get("http://localhost:3000/employees.json").then((response) => {
console.log("Hello my Minions");
console.log(response.data);
setEmployees(response.data);
});
};
useEffect(handleIndexEmployees, []);
return (
<div>
<EmployeesAxios employee={employees} />
</div>
);
}
export function EmployeesAxios(props) {
return (
<div>
<h1>Employees</h1>
{props.employee.map((employee) => {
<div key={employee.id}>
<h3>Name: {` ${employee.first_name} ${employee.last_name}`}</h3>
</div>;
})}
</div>
);
}
{props.todos.map((todo) => (
<div key={todo.id}>
<h2>{todo.name}</h2>
<p>{todo.description}</p>
<button
onClick={() => {
axios.patch(`http://localhost:3000/todos/${todo.id}.json`).then((response) => {
console.log(response);
});
}}
>
{todo.done}
Done
</button>
<button onClick={() => props.onShowTodo(todo)}>More info</button>
</div>
))}
I tried putting this all on one page with making all the requests on that page, and I get the same response with that so my page that I am rendering for a selector wheel to assign tasks to that employee works just fine with using props so that is why I tried moving all the axios requests to another page and passing in props. I also tried using isLoading as well and this didn't work either.
It looks like you are not returning anything from your map.
Add a return statement here
{
props.employee.map((employee) => {
// Return something from the map
return (
<div key={employee.id}>
<h3>Name: {` ${employee.first_name} ${employee.last_name}`}</h3>
</div>
);
});
}
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 am taking a React course in which week by week we have certain challenges that lead us to create an E-Commerce.
My problem is that, I have the data of a product hardcoded, when entering the page useEffect creates a promise that is resolved in 2 seconds using setTimeOut and returns the product data.
In a previous challenge I already did essentially the same thing, only having an array with several products and following the design pattern of: ItemListContainer asks for the data, passes the data to ItemList, applies .map() to the array and for each item creates an Item component sending by props the data.
In the current challenge as we are working with a single product we do not have to do .map() on any array, and this for some reason causes the Item component (in this case called ItemDetail) to render before the data arrives, although it only renders the parts that do not depend on the data arriving to it.
Demo: Demo (It renders the styled div and the "$" sign).
After several hours looking at the code I can't figure out why it happens and what I could do to fix it.
Github repo: enzom-uy/coderhouseECommerce
ItemDetailContainer code:
import React, { useState, useEffect } from 'react'
import ItemDetail from '../ItemDetail/ItemDetail'
const productData = {
id: '4',
name: 'Cuarto producto',
description: 'Descripcion del cuarto producto',
price: 10,
pictureUrl:
'https://media.istockphoto.com/vectors/thumbnail-image-vector-graphic-vector-id1147544807?k=20&m=1147544807&s=612x612&w=0&h=pBhz1dkwsCMq37Udtp9sfxbjaMl27JUapoyYpQm0anc='
}
const ItemDetailContainer = () => {
const [detailedProduct, setDetailedProduct] = useState({})
useEffect(() => {
const fetchingData = new Promise((res, rej) => {
setTimeout(() => {
res(productData)
}, 2000)
})
fetchingData.then((res) => {
setDetailedProduct(res)
console.log('Se guardaron los datos')
})
fetchingData.catch((err) => {
console.log('Failed')
})
}, [])
return (
<>
<h1>Producto detallado</h1>
<ItemDetail product={detailedProduct} />
</>
)
}
export default ItemDetailContainer
ItemDetail code:
import React from 'react'
export default function ItemDetail({ product }) {
return (
<div className="bg-slate-100 w-60 flex flex-col items-center mx-1 px-2 border border-slate-400 text-center">
<span>{product.name}</span>
<img src={product.pictureUrl} width="120" />
<p>{product.description}</p>
<span>${product.price}</span>
</div>
)
}
The Short Answer
It is showing the styled div and the dollar sign because that is what you have included in the JSX. To remove it until the data arrives, you can make it a part of the return statement conditionally on the data being present. That might look something like this (using the simpler of the options listed later in my answer)
return (
<>
<h1>Producto detallado</h1>
{detailedProduct.name && <ItemDetail product={detailedProduct} />}
</>
)
Why It's Happening
If you have a simple component that doesn't rely on data, then it will render everything in the return statement. In the below example, that is the div, h1, and p element.
function Parent() {
return (
<div>
<h1>Hello!</h1>
<p>We have no products to sell.</p>
</div>
);
}
If you have a component that does rely on data, say from an API (or a setTimeout), then that doesn't change. What is included in the return statement (including any child components) will be rendered.
function Parent() {
const [data, setData] = useState({});
useEffect(() => {
fetch('https://api.com')
.then(res => res.json())
.then(setData)
.catch(err => console.error(err));
}, []);
return (
<div>
<h1>Hello!</h1>
<Child data={data} />
</div>
);
}
function Child({ data }) {
return (
<p>The name is: {data.name}</p>
);
}
In this case, before the data comes back from the API, the child element's <p> element with the text "The name is: " will appear, since it is a part of the return. Then, when the data arrives, it will re-render and the name will appear. If we want it to say something else, we can do that with something like this...
function Child({ data }) {
return (
<p>{data.name ? `The name is: ${data.name}` : "Loading name..."}</p>
);
}
This will ask the component, "is there a value for the data.name property? If yes, then display the text "The name is: ___" where the name is filled in the blank. If no, then display the text "Loading name..."
This is also option 1 for your solution. You can use a ternary operator to provide a default value when the data has not yet arrived.
Another option is to just remove the entire component that relies on the data. Since the return statement will only return items that are truth-y, you can conditionally render your ItemDetail component. Our example might look something like this...
function Parent() {
const [data, setData] = useState({});
useEffect(() => {
fetch('https://api.com')
.then(res => res.json())
.then(setData)
.catch(err => console.error(err));
}, []);
return (
<div>
<h1>Hello!</h1>
{data.name && <Child data={data} />}
</div>
);
}
function Child({ data }) {
return (
<p>The name is: {data.name}</p>
);
}
This is option 2 for your solution. It is quick and easy, but there are distinct differences between how they impact developer and user experience.
As an option 3, you could also implement a loading component that would take the place of the product component (including something like a spinner or progress bar), but I think it may be more work than your React course calls for.
Important Note. While the second option is definitely easier since you don't have to code default values for every field, it can sometimes be worse for user experience, since entire components will appear and disappear when data loads in, which can cause big layout shifts.
You can do a few things like:
Don't render your component until the request is done or...
Use loading flag state
Use an extra state for the "loading" flag.. you can start this on "true" and after finished change it to false and render your component with the data.
This is my example:
import React, { useState, useEffect } from "react";
const productData = {
id: "4",
name: "Cuarto producto",
description: "Descripcion del cuarto producto",
price: 10,
pictureUrl:
"https://media.istockphoto.com/vectors/thumbnail-image-vector-graphic-vector-id1147544807?k=20&m=1147544807&s=612x612&w=0&h=pBhz1dkwsCMq37Udtp9sfxbjaMl27JUapoyYpQm0anc="
};
const ItemDetailContainer = () => {
const [detailedProduct, setDetailedProduct] = useState({});
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchingData = new Promise((res, rej) => {
setTimeout(() => {
res(productData);
}, 2000);
});
fetchingData.then((res) => {
setDetailedProduct(res);
setLoading(false);
console.log("Se guardaron los datos");
});
fetchingData.catch((err) => {
console.log("Failed");
});
}, []);
if (loading) return "Cargando...";
return (
<>
<h1>Producto detallado</h1>
<ItemDetail product={detailedProduct} />
</>
);
};
export default ItemDetailContainer;
function ItemDetail({ product }) {
return (
<div className="bg-slate-100 w-60 flex flex-col items-center mx-1 px-2 border border-slate-400 text-center">
<span>{product.name}</span>
<img src={product.pictureUrl} width="120" />
<p>{product.description}</p>
<span>${product.price}</span>
</div>
);
}
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>;
};