Why is map() only displaying the last object in the array? - javascript

I am currently learning React and I am trying to render my JSON data to the page. I used the map(), however it is only rendering the last object from the array onto the page.
class ContentCard extends Component {
state = {
products: ProductInfo
};
render() {
console.log(this.state.products);
return (
<>
{this.state.products.map(items => (
<div className="container page-wrapper">
<div className="page-inner">
<div className="row">
<div className="el-wrapper">
<div className="box-up">
<img className="img" src={items.img} alt="" />
<div className="img-info">
<div className="info-inner">
<span className="p-name">{items.name}</span>
<span className="p-company">{items.company}</span>
<span className="price">
{items.price.toLocaleString("en-US", {
style: "currency",
currency: "USD"
})}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
))}
</>
);
}
}
Here is my Json Data. The image links would have been too long so i just added a sample image to make it easier to read.
const products = [
{
id: 1000,
company: "YEEZY",
categoryName: "Men",
category: "tShirt",
name: "I feel like Pablo",
img: "http://code.slicecrowd.com/labs/4/images/t-shirt.png",
price: 120.0
},
{
id: 1000,
company: "H&M",
categoryName: "Men",
category: "tShirt",
name: "butterfly",
img: "http://code.slicecrowd.com/labs/4/images/t-shirt.png",
price: 120.0
},
{
id: 1000,
company: "North Face",
categoryName: "Men",
category: "Jacket",
name: "WindBreaker",
img: "http://code.slicecrowd.com/labs/4/images/t-shirt.png",
price: 120.0
}
];
export default products;

You need to add a key when you render multiple items in an array.
array.map(item => (
<div key={item.unique_property}>
[...]
</div>
))
Also, are you definitely on React 16.2 + ? I.e. do you have the necessary Fragment syntax support?

This example could help you:
import React, { useState, useEffect } from 'react';
const MyPost = () => {
const [post, setPost] = useState([]);
useEffect(() => {
fetch('url')
.then(res => res.json())
.then(jsonData => setPost([...jsonData]));
}, []);
return (
<>
{post.length > 0 ? (
<ol>
{post.map(p => (
<li key={p.replace(/' '/g, '')}>{p}</li>
))}
</ol>
) : (
<h1>No post Available</h1>
)}
</>
);
};
export default MyPost;

Related

ReactJs does not render when I use props

There seems to be a problem with the function Card(props).
The output is fine if I replace the function's content with
<img src="data[0].img" instead of props.img, etc.
var data =
[{
id: 1,
name: "tokyo",
descr: "a",
img: "/tokyo.png"
}
]
function Card(props) {
return (
<div>
<img src={props.img}/>
<h1> {props.name}</h1>
<p> {props.descr}</p>
</div>
)
}
const cards = data.map(item => {
return (<Card
img={item.img}
name={item.name}
descr={item.descr}
key={item.id} />)
})
ReactDOM.render({cards},document.getElementById("root"))

React JS Mapping Array and Unpacking Specific Values

I am trying to map over specific values in an array of objects.
I have collected data from my Backend API and this is the response:
// An array of objects
console.log(response.data) =
[
{name:"Bryan",age:"25",sport:"basketball"},
{name:"Justin",age:"30",sport:"soccer"},
{name:"Mark",age:"28",sport:"basketball"},
{name:"Cowell",age:"27",sport:"soccer"},
]
I put this data into a state ("data") using "useState()" from React and used a useEffect to unpack the data upon rendering.
const [data, setData] = useState([])
// some function to store response.data in state
setData(response.data)
I want to map these values onto my Browser in the following way such that Soccer players are displayed in the first div and Basketball players in the second div:
(tried several ways but they resulted in parsing errors)
function App() {
const [data, showData] = useState([])
return (
<div>
{data.map(info => {
<div>
<h1> Sport: soccer </h1>
<h5> {info.name} </h5>
</div>
<div>
<h1> Sport: basketball</h1>
<h5> {info.name} </h5>
</div>
}
)}
</div>
)
}
I am trying to group the names within the same div block (same sport) and not 2 different div blocks for each sport.
You need to return the elements from the map function and also remove the part where you hardcode basketball.
{data.map((info, idx) => (
<div key={idx}>
<h1> Sport: {info.sport} </h1>
<h5> {info.name} </h5>
</div>
))}
const groupBy = (array, getGroupByKey) => {
return (
(array &&
array.reduce((grouped, obj) => {
const groupByKey = getGroupByKey(obj);
if (!grouped[groupByKey]) {
grouped[groupByKey] = [];
}
grouped[groupByKey].push(obj);
return grouped;
}, {})) ||
{}
);
};
const App = (props) => {
var [data, setData] = React.useState([
{ name: 'Bryan', age: '25', sport: 'basketball' },
{ name: 'Justin', age: '30', sport: 'soccer' },
{ name: 'Mark', age: '28', sport: 'basketball' },
{ name: 'Cowell', age: '27', sport: 'soccer' },
]);
const players = groupBy(data, (player) => player.sport);
const sportKeys = Object.keys(players);
return (
<div>
{sportKeys.map((info, idx) => (
<div key={idx}>
<h1> Sport: {info} </h1>
{players[info].map((player, i) => (
<h5 key={i}>{player.name}</h5>
))}
</div>
))}
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="root"></div>

I am building a nested tree data card using react but I am stuck, my requirement is to solve using react only

My goal is to design card as in the picture using react:
The requirements are:
As above picture as reference
In this implementation,
a feature's sub-features only expand when selected on and only the leaves contribute to the overall
cost.
A feature may have N sub-features. In other words, features and their sub-features are
arbitrarily nested.
So far I could do the expanding part, and to contribute total cost but the task of propagating cost to parent from child in the tree is where I got stuck, Any suggestions could be really helpful.
Example:
parent1 (-) (80)
child1 (-) (80) (when user checks both nested child values sum should)
nestedChild1 (50) (propagate to parents)
nestedChild2 (30)
parent2 (-) (10)
child1 (10)
Total should be 80 + 10 = 90 (sum of Parents)
This is codesandbox link for what I have worked on so far:
This is my Card Component:
import './Card.css';
import { useState } from 'react';
import { data } from '../../utils/data';
import CardItem from '../CardItem/CardItem';
const Card = () => {
const [price, setPrice] = useState({});
const total = Object.values(price).reduce(
(total, current) => total + current,
0
);
return (
<div className="card">
<div className="card__container">
<h4>
<b>Subscription Preferences</b>
</h4>
<hr />
<CardItem items={data} setPrice={setPrice} price={price} />
</div>
<div className="footer">
<hr />
<div className="footer__container">
<p>Total: ${total} / mo</p>
<button>Save</button>
</div>
</div>
</div>
);
};
export default Card;
This my CardItem Component:
import './CardItem.css';
import { useState } from 'react';
const CardItem = ({ items, setPrice, price }) => {
const [displayChildren, setDisplayChildren] = useState({});
return (
<div>
{items.map((item) => (
<ul key={item.name}>
<li>
<input
type="checkbox"
onChange={(e) => {
setDisplayChildren({
...displayChildren,
[item.id]: !displayChildren[item.id],
});
setPrice((price) => ({
...price,
[item.id]: e.target.checked ? +e.target.value : 0,
}));
}}
value={item.value ? item.value : 0}
id={item.id}
/>
<label htmlFor={item.id}>
{item.name}
{item.value ? ` ($${item.value})` : ' (-)'}
</label>{' '}
{displayChildren[item.id] && item.items && (
<CardItem items={item.items} setPrice={setPrice} />
)}
</li>
</ul>
))}
</div>
);
};
export default CardItem;
Sample Data:
{
id: 1,
name: 'List title 1',
items: [
{
id: 11,
name: 'List item 1_1',
items: [
{
id: 111,
name: 'List item 1_1_1',
value: 50,
},
{
id: 112,
name: 'List item 1_1_2',
value: 35,
},
],
},
{
id: 12,
name: 'List item 1_2',
value: 30,
},
],
},
{
id: 2,
name: 'List title 2',
items: [
{
id: 21,
name: 'List item 2_1',
value: 15,
},
{
id: 22,
name: 'List item 2_2',
value: 35,
},
],
}
Issue
Each depth of options has its own price state and there's no way to pass each individual price value back up the tree to a parent node for summation.
Solution
Move the price state up/out to the Card component and pass the setPrice updater function to the CardItem to be passed to each level of recursion.
Card.js
const Card = () => {
const [price, setPrice] = useState({}); // <-- lift price state here
const total = Object.values(price).reduce( // <-- compute derived total
(total, current) => total + current,
0
);
return (
<div className="card">
<div className="card__container">
<h4>
<b>Subscription Preferences</b>
</h4>
<hr />
<CardItem items={data} setPrice={setPrice} /> // <-- pass setPrice callback
</div>
<div className="footer">
<hr />
<div className="footer__container">
<p>Total: ${total} / mo</p> // <-- render total
<button>Save</button>
</div>
</div>
</div>
);
};
CardItem.js
const CardItem = ({ items, setPrice }) => { // <-- receive setPrice callback
const [displayChildren, setDisplayChildren] = useState({});
return (
<div>
{items.map((item) => (
<ul key={item.name}>
<li>
<input
type="checkbox"
onChange={(e) => { // <-- change onClick to onChange
setDisplayChildren({
...displayChildren,
[item.id]: !displayChildren[item.id]
});
setPrice((price) => ({ // <-- update price on checked or not
...price,
[item.id]: e.target.checked ? +e.target.value : 0
}));
}}
value={item.value ? item.value : 0}
id={item.id}
/>
<label htmlFor={item.id}>
{item.name}
{item.value ? ` ($${item.value})` : " (-)"}
</label>{" "}
{displayChildren[item.id] && item.items && (
<CardItem items={item.items} setPrice={setPrice} /> // <-- pass setPrice
)}
</li>
</ul>
))}
</div>
);
};
Demo
Link to forked sandbox with computing intermediate subtotals.

Why does this warning keep appearing when there is already a key prop? Warning: Each child in a list should have a unique "key" prop

I'm new with React and I don't understand why this warning,
Warning: Each child in a list should have a unique "key" prop
keeps appearing when there is already a key prop on the element?
I'm using an NPM package called react-horizontal-scrolling-menu and in the package it uses JavaScript and I'm using Typescript in my React project if that makes any difference.
const list: any[] = ["items", "item2"];
const MenuItem = ({ text, selected }: {text: string, selected: string}) => {
return <div
className={`menu-item ${selected ? 'active' : ''}`}
>{text}</div>;
}
const selected: any = 'item1';
export const Menu = (list: any, selected: any) => {
list.map((el: any, index: any) => {
const { name } = el;
return <MenuItem text={name} key={name} selected={selected} />;
})
}
const Arrow = ({ text, className }: {text: string, className: any}) => {
return (
<div
className={className}
>{text}</div>
);
};
const ArrowLeft = Arrow({ text: '<', className: 'arrow-prev' });
const ArrowRight = Arrow({ text: '>', className: 'arrow-next' });
class RestaurantListIndex extends Component {
private menuItems: any;
constructor(props: any) {
super(props);
this.menuItems = Menu(list, selected);
}
state = {
selected,
// restaraunts: [
// { name: 'Ashish 11 Restaurant', type: 'North Indian, Punjabi, Chinese', rating: 4.9, deliveryTime: 45, },
// { name: 'Ashish 11 Restaurant', type: 'North Indian, Punjabi, Chinese', rating: 4.9, deliveryTime: 45, },
// { name: 'Ashish 11 Restaurant', type: 'North Indian, Punjabi, Chinese', rating: 4.9, deliveryTime: 45, },
// ],
menu: [{ imageUrl: "", name: "Testing", ingredients: "" },]
};
onSelect = (key: any) => {
this.setState({ selected: key });
}
render() {
const { selected } = this.state;
const menu = this.menuItems;
return (
<div>
<div className="pb-5 pt-3" style={{ backgroundColor: "#2D2A4B" }}>
<div className="view">
<div className="row">
<div className="col">
<h1 className="font-weight-bold text-white">Logo</h1>
</div>
<div className="col-auto">
<h6 className="text-white">icon</h6>
</div>
<div className="col-auto">
<h6 className="text-white">Login</h6>
</div>
</div>
<ScrollMenu
data={menu}
arrowLeft={ArrowLeft}
arrowRight={ArrowRight}
selected={selected}
onSelect={this.onSelect}
/>
</div>
</div>
<div className="view mt-3">
<div className="row mt-5">
<div className="col">
<h2>Menu</h2>
</div>
<div className="col-auto">
<h5>Delivery Time: <strong>45 minutes</strong></h5>
</div>
</div>
<div className="row mt-5 pt-5">
{this.state.menu.map((menuItem) => {
return <MenuItemCard
imageUrl={menuItem.imageUrl}
name={menuItem.name}
ingredients={menuItem.ingredients}
/>
})}
</div>
</div>
</div>
);
}
}
export default RestaurantListIndex;
You are missing a key here
<div className="row mt-5 pt-5">
{this.state.menu.map((menuItem) => {
return <MenuItemCard
key={menuItem.name}
imageUrl={menuItem.imageUrl}
name={menuItem.name}
ingredients={menuItem.ingredients}
/>
})}
</div>

How to display data coming from mongodb to reactjs after clicking the button?

I am making react project using express.js, mongodb, react.js, and node.js. and trying to fetch data from backend api which is running on port 5000.
When I check the api using postman, it is working. And the data is showing in the browser's console. Also, when I press Get button as given in the code below, it doesn't work on the browser. But I'm able to see the array data in the browser's console.
<Button onClick={()=><li>{employeeItem}</li>}>Get</Button>
Here is my full code:
import React, { Component } from "react";
import {
form,
FormGroup,
FormControl,
ControlLabel,
Button
} from "react-bootstrap";
import "./App.css";
import { stringify } from "querystring";
class App extends Component {
constructor(props) {
super(props);
this.AddName = this.AddName.bind(this);
this.AddContact = this.AddContact.bind(this);
this.AddAge = this.AddAge.bind(this);
this.state = {
name: "",
contact: "",
age: "",
employees: []
};
}
AddName(e) {
this.setState({ name: e.target.value });
}
AddContact(e) {
this.setState({ contact: e.target.value });
}
AddAge(e) {
this.setState({ age: e.target.value });
}
componentWillMount() {
fetch("http://localhost:5000/api/employees")
.then(res => res.json())
.then(data => this.setState({ employees: data }));
}
render() {
const employeeItem = this.state.employees.map(employee => (
<div key={employee._id}>
<h3>{employee.name}</h3>
<p>{employee.contact}</p>
<p>{employee.age}</p>
</div>
));
return (
<div className="App">
<header className="App-header">
<h1 className="App-title">Employee List</h1>
</header>
<div className="Layout">
<form>
<FormGroup>
<ControlLabel>Name:</ControlLabel>
<FormControl
type="text"
value={this.state.name}
placeholder="Employee name"
onChange={this.AddName}
/>
<div>
<ControlLabel>Contact:</ControlLabel>
<FormControl
type="number"
value={this.state.contact}
placeholder="Mobile number"
onChange={this.AddContact}
/>
</div>
<div>
<ControlLabel>Age:</ControlLabel>
<FormControl
type="number"
value={this.state.age}
placeholder="Age"
onChange={this.AddAge}
/>
</div>
</FormGroup>
<Button type="submit">Add</Button>
<Button onClick={() => console.log({ employeeItem })}>Get</Button>
<Button type="submit">Delete</Button>
</form>
</div>
</div>
);
}
}
export default App;
on running page
You can't render an item as you are trying inside an onClick callback. You can render the items immediately after fetched them or you can trigger with an onClick the fetch or you can hide and show the items.
Immediately rendering
const employees = [
{ _id: 1, name: "foo", contact: "abc", age: 20 },
{ _id: 2, name: "bar", contact: "efg", age: 30 },
{ _id: 3, name: "baz", contact: "hij", age: 40 }
];
const fakeRequest = () =>
new Promise(resolve => setTimeout(() => resolve(employees), 1000));
class App extends React.Component {
state = {
employees: []
};
componentDidMount() {
fakeRequest().then(employees => this.setState({ employees }));
}
render() {
const employees = this.state.employees.map(employee => (
<div style={{ border: "1px solid black" }} key={employee._id}>
<h3>Name: {employee.name}</h3>
<p>Contact: {employee.contact}</p>
<p>{employee.age}</p>
</div>
));
return (
<div>
<p>Data will be fetched in a second automatically.</p>
{employees}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Get with a button click
const employees = [
{ _id: 1, name: "foo", contact: "abc", age: 20 },
{ _id: 2, name: "bar", contact: "efg", age: 30 },
{ _id: 3, name: "baz", contact: "hij", age: 40 },
];
const fakeRequest = () => new Promise( resolve =>
setTimeout( () => resolve( employees ), 1000)
);
class App extends React.Component {
state = {
employees: [],
};
getEmployees = () =>
fakeRequest()
.then(employees => this.setState({ employees }))
render() {
const employees = this.state.employees.map(employee => (
<div style={{ border: "1px solid black"}} key={employee._id}>
<h3>Name: {employee.name}</h3>
<p>Contact: {employee.contact}</p>
<p>{employee.age}</p>
</div>
));
return (
<div>
<p>Data will be fetched after the button click.</p>
<button onClick={this.getEmployees} >Get Employees</button>
{employees}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Show/hide method
const employees = [
{ _id: 1, name: "foo", contact: "abc", age: 20 },
{ _id: 2, name: "bar", contact: "efg", age: 30 },
{ _id: 3, name: "baz", contact: "hij", age: 40 },
];
const fakeRequest = () => new Promise( resolve =>
setTimeout( () => resolve( employees ), 1000)
);
class App extends React.Component {
state = {
employees: [],
showEmployees: false,
};
componentDidMount() {
fakeRequest()
.then(employees => this.setState({ employees }))
}
toggleEmployees = () => this.setState( prevState => ({
showEmployees: !prevState.showEmployees,
}))
render() {
const { showEmployees } = this.state;
const employees = this.state.employees.map(employee => (
<div style={{ border: "1px solid black"}} key={employee._id}>
<h3>Name: {employee.name}</h3>
<p>Contact: {employee.contact}</p>
<p>{employee.age}</p>
</div>
));
return (
<div>
<p>Data will be fethced automatically in a second but will be hidden by default. Button click toggles this state.</p>
<button
onClick={this.toggleEmployees}
>
{
showEmployees ? "Hide Employees" : "Show Employees"
}
</button>
{this.state.showEmployees && employees}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Categories

Resources