React doesn't send entries to DOM - javascript

I am making an AJAX request inside my export default class List extends React Component. It works fine and I see the requested array being returned in the form of:
[{...}, {...}, ...]
where each object inside the return is structured like this:
{
description: "bla",
id: "5b4915c37ae56418fcddb88f",
link: "www.website.com",
name: "bla",
position: "bla",
requirements: {
age: 99,
citizenship: "bla",
degree: "bla"
},
salary: "1",
state: "bla"
}
The return is set in the component's state. My render function looks like this:
render() {
let entries = this.state.listEntries
let mapper = entries.forEach((item,index) => {
console.log(entries)
return (
<li key={index}>
{item.name}
{item.position}
</li>
)
})
return (
<div className="listWrapper">
<HeaderBar props={this.state.props}/>
<ul className="list">
{mapper}
</ul>
</div>
)
}
React doesn't show the item.name or item.position, or any other item entry in the DOM. It seems like such a simple problem, but I just can't figure out what the issue is. There is no error in the console, it just doesn't display.

As forEach does not return anything you should use map for this kind of situations. Also, using index as a key can cause some problems, so you can use item.id instead of index.
class App extends React.Component {
state = {
listEntries: [
{
description: "bla",
id: "5b4915c37ae56418fcddb88f",
link: "www.website.com",
name: "bla1",
position: "bla1",
requirements: {
age: 99,
citizenship: "bla",
degree: "bla"
},
salary: "1",
state: "bla"
},
{
description: "bla",
id: "5b4915c37ae56468fcddb88f",
link: "www.website.com",
name: "bla2",
position: "bla2",
requirements: {
age: 99,
citizenship: "bla",
degree: "bla"
},
salary: "1",
state: "bla"
}
]
}
render() {
let entries = this.state.listEntries
let mapper = entries.map((item) => {
console.log(entries)
return (
<li key={item.id}>
{item.name}
{item.position}
</li>
)
})
return (
<div className="listWrapper">
<ul className="list">
{mapper}
</ul>
</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>

Related

Loop inside loop in redux store

I have a simple state:
const INITIAL_PEOPLE = {
name: 'Friends',
list: [
{
id: 1,
name: 'Alexander',
child: [
{
id: 2,
name: 'Romuald'
},
{
id: 3,
name: 'Vanessa'
}
]
},
{
id: 4,
name: 'Alex',
child: [
{
id: 5,
name: 'Jessica'
}
]
}
]
}
Now I want to print that. Look at my code
{prople.list.map(person =>
<li key={person.id}>{person.name}
{person.child.map(child =>
{child.name}
)}
</li>
)}
First loop works correctly but if I added second loop (child), console catch an error that person.child is undefined. Why it doesn't work?
You were not returning any value from your map function.
{INITIAL_PEOPLE.list.map((person) => {
return (
<li key={person.id}>
{person.name}
{person.child.map((child) => {
return child.name;
})}
</li>
);
})}
Your code should be written like this with a check if person have a child array.
{prople.list.map(person =>
<li key={person.id}>{person.name}
{person.child && person.child.length && person.child.map(child =>
{return child.name }
)}
</li>
)}
Here before calling the map function for child, we are checking if person has an attribute named child and if child is an array by checking if child has a property length by person.child.length. Notice the return part in the nested map.

Issues with an object- Will not map

Whenever I run my React program, I receive the following error:
Objects are not valid as a React child (found: object with keys {mappedCats}). If you meant to render a collection of children, use an array instead.
in choicesList (at App.js:33)
What I am trying to do is take the FEATURES object, iterate through it and create a div for each category. Then iterate through the items of that category to show the name and cost of each item. I tried converting the object to an array, but it doesn't seem to be working. Originally I tried splitting this up into multiple components but I think I was overly ambitious.
The Categories component just takes the props and puts them into a div and paragraphs.
If somebody could point out my mistake I would appreciate it. Thank You
import React from 'react'
import Categories from './categories'
const choicesList = (props) => {
const FEATURES = {
Processor: [
{
name: '17th Generation Intel Core HB (7 Core with donut spare)',
cost: 700
},
{
name: 'Professor X AMD Fire Breather with sidewinder technology',
cost: 1200
}
],
"Operating System": [
{
name: 'Ubuntu Linux 16.04',
cost: 200
},
{
name: 'Bodhi Linux',
cost: 300
}
],
"Video Card": [
{
name: 'Toyota Corolla 1.5v',
cost: 1150.98
},
{
name: 'Mind mild breeze 2000',
cost: 1345
}
],
Display: [
{
name: '15.6" UHD (3840 x 2160) 60Hz Bright Lights and Knobs',
cost: 1500
},
{
name: '15.3" HGTV (3840 x 2160) Home makeover edition',
cost: 1400
},
]
};
const mappedCats = Object.keys(FEATURES).map((cat) => {
return (
<div>
<h1>{cat}</h1>
{Object.keys(FEATURES[cat]).map((item, idx) => {
return (
<Categories name={FEATURES[cat][idx].name} cost={FEATURES[cat][idx].cost}/>
)
})}
</div>
)
})
return(
{mappedCats}
)
}
export default choicesList
Because React components must render a single root element, you'd need to wrap it in either a fragment or an element:
return (
<>{ mappedCats }</>
)
As raw js (not enclosed in markup) your render method is returning an object literal:
// this is shorthand for { mappedCats: mappedCats }
return { mappedCats };
mappedCats itself is a valid element(s). Just return that
return mappedCats
const ChoicesList = (props) => {
const FEATURES = {
Processor: [
{
name: "17th Generation Intel Core HB (7 Core with donut spare)",
cost: 700,
},
{
name: "Professor X AMD Fire Breather with sidewinder technology",
cost: 1200,
},
],
"Operating System": [
{
name: "Ubuntu Linux 16.04",
cost: 200,
},
{
name: "Bodhi Linux",
cost: 300,
},
],
"Video Card": [
{
name: "Toyota Corolla 1.5v",
cost: 1150.98,
},
{
name: "Mind mild breeze 2000",
cost: 1345,
},
],
Display: [
{
name: '15.6" UHD (3840 x 2160) 60Hz Bright Lights and Knobs',
cost: 1500,
},
{
name: '15.3" HGTV (3840 x 2160) Home makeover edition',
cost: 1400,
},
],
};
const mappedCats = Object.keys(FEATURES).map((cat) => {
return (
<div>
<h1>{cat}</h1>
{Object.keys(FEATURES[cat]).map((item, idx) => {
return (
<div>
{`name: ${FEATURES[cat][idx].name}`}
<br></br>
{`cost: ${FEATURES[cat][idx].cost}`}
</div>
);
})}
</div>
);
});
return mappedCats;
//return (<div>{ mappedCats }</div>);
};
const domContainer = document.querySelector('#app');
ReactDOM.render(<ChoicesList />, domContainer);
<script crossorigin src="https://unpkg.com/react#16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.production.min.js"></script>
<div id="app"> </div>

Need to pass back customer values to a POST action in React

I have a form that displays customer data based on a JSON object received from an initial GET request. I map that information out to display both the customer details and their available offers and I've created an onClick function to highlight their selected offer which maps offerId to an object I've defined in state called cardActive. What I'm trying to do now is create a second onClick function connected to a submit button that fires a POST action returning the following values as the JSON body:
{
"CustomerId" : "1",
"SessionId" : "9636",
"Offer": {
"OfferId" : "1",
"OfferName" : "Business Internet 75"
}
}
I've included my current component below including a dummy JSON object that mimics the response back from that initial GET request
class UsersList extends Component {
constructor() {
super();
this.selectCard = this.selectCard.bind(this);
this.state = {
cardActive: null,
offerName: null,
customerId: null,
customers: [
{
CustomerId: "1",
LastName: "Doe",
FirstName: "Jane",
Address: {
Address1: "1811 Chestnut Street",
Address2: null,
City: "Philadelphia",
State: "Pennsylvania",
Zip: "19103"
},
Offers: [
{
OfferId: "Offer1",
Name: "Offer Number 1",
Products: [
{
ProductId: 1,
ProductName: "Cool stuff"
},
{
ProductId: 2,
ProductName: "Some little stuff"
}
],
Price: "$1"
},
{
OfferId: "Offer2",
Name: "Offer Number 2",
Price: "$2",
Products: [
{
ProductId: 3,
ProductName: "More stuff"
},
{
ProductId: 4,
ProductName: "Hey theres stuff here"
}
]
},
{
OfferId: "Offer3",
Name: "Offer Number 3",
Price: "$3",
Products: [
{
ProductId: 5,
ProductName: "Check out this stuff"
},
{
ProductId: 5,
ProductName: "More stuff"
}
]
}
]
}
]
};
}
selectCard(offerId) {
this.setState({ cardActive: offerId });
}
render() {
return (
<div>
{this.state.customers.map((customer, index) => {
return (
<div key={index + customer.CustomerId}>
<h3>
Name: {customer.LastName}, {customer.FirstName}
</h3>
<h3>Customer ID: {customer.CustomerId}</h3>
<h3>
Address:
<br />
{customer.Address.Address1}
<br />
{customer.Address.City}, {customer.Address.State}{" "}
{customer.Address.Zip}
</h3>
<br />
<h2>Available Offers</h2>
<Grid container spacing={24} justify="center">
{customer.Offers.map((Offer, index) => {
return (
<div
key={index + Offer.OfferId}
onClick={() => this.selectCard(Offer.OfferId)}
>
<Grid item xs={12}>
<div
className={
Offer.OfferId === this.state.cardActive
? "cardActive"
: "card"
}
>
<div className="container">
<h5>
<b>{Offer.OfferId}</b>
</h5>
<h2>{Offer.Name}</h2>
{Offer.Products.map((Product, index) => {
return (
<div key={index + Product.ProductId}>
<p>+ {Product.ProductName}</p>
</div>
);
})}
<h3>{Offer.Price}</h3>
</div>
</div>
</Grid>
</div>
);
})}
</Grid>
</div>
);
})}
<button className="navbuttonSelected">Submit</button>
</div>
);
}
}
export default UsersList;
I added the objects into the state that I'm hoping to populate (offerName, CustomerId and keeping cardActive as the holder for offerId) and now I'm trying to figure out how to write the function to map all of those values to send back via POST. The best I've come up with so far is
submitSelection(offerId, offerName, CustomerId) {
this.setState({
cardActive: offerId,
offerName: offerName,
CustomerId,
SessionId: SessionId
});
}
Any suggestions/examples on how to do this would be a huge help
I think that the best way to handle the data form is by a state property in this case will be something like this :
this.state = {
dataModel:{
cardActive : null,
offerName : null,
customerId : null,
CustomerId : null,
SessionId : null,
},
...
}
]
};
So in your submit function you can just update the data that youve received to the this.state.dataModel.
then you can use axios or somthing to send the dataModel to as the data of the axios method.
Check my sandbox maybe is clearer that way:
https://codesandbox.io/s/8xm00xplm2

React mapping menu from JSON

I have a scrolling menu items, and the titles of each item is hardcoded into a const, along side with the id
const list = [
{ name: "category1", id: 0 },
{ name: "category2", id: 1 },
{ name: "category3", id: 2 },
{ name: "category4", id: 3 },
{ name: "category5", id: 4 },
{ name: "category6", id: 5 },
{ name: "category7", id: 6 },
{ name: "category8", id: 7 }
];
I have a json file that contains the category name for each child:
{
"results": [
{
"category": "category1",
"name": {
"title": "mr",
"first": "ernesto",
"last": "roman"
},
"email": "ernesto.roman#example.com",
"id": {
"name": "DNI",
"value": "73164596-W"
},
"picture": {
"large": "https://randomuser.me/api/portraits/men/73.jpg",
"medium": "https://randomuser.me/api/portraits/med/men/73.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/men/73.jpg"
}
},
{
"category": "category2",
"name": {
"title": "mr",
"first": "adalbert",
"last": "bausch"
},
"email": "adalbert.bausch#example.com",
"id": {
"name": "",
"value": null
} etc....
I want to show these categories "category": "category1", as the titles of my menu, I now that I need to start stateless and add them from the JSON, the fetching part from the JSON is done locally in componentDidMount, but I am not sure how can I map them into appearing as menu names to make the menu dynamic, I basically want the same output but from the json not hardcoded. here is a sandbox snippet, would appreciate the help.
https://codesandbox.io/s/2prw4j729p?fontsize=14&moduleview=1
Just convert the JSON output to an object like list with a map function from the results and then set is as MenuItems on the state, which is what you pass to the function on render(). Like that.
import React, { Component } from "react";
import ScrollMenu from "react-horizontal-scrolling-menu";
import "./menu.css";
// One item component
// selected prop will be passed
const MenuItem = ({ text, selected }) => {
return (
<div>
<div className="menu-item">{text}</div>
</div>
);
};
// All items component
// Important! add unique key
export const Menu = list =>
list.map(el => {
const { name, id } = el;
return <MenuItem text={name} key={id} />;
});
const Arrow = ({ text, className }) => {
return <div className={className}>{text}</div>;
};
export class Menucat extends Component {
state = {
selected: "0",
MenuItems: []
};
componentDidMount() {
fetch("menu.json")
.then(res => res.json())
.then(result => {
const items = result.results.map((el, idx) => {
return { name: el.category, id: idx };
});
this.setState({
isLoaded: true,
MenuItems: items
});
});
}
render() {
const { selected, MenuItems } = this.state;
// Create menu from items
const menu = Menu(MenuItems, selected);
return (
<div className="App">
<ScrollMenu
data={menu}
selected={selected}
onSelect={this.onSelect}
alignCenter={true}
tabindex="0"
/>
</div>
);
}
}
export default Menucat;
Cheers!
Looks like you don't have to hard code your category list at all. In your componentDidMount() fetch the json and group the results into separate categories like this:
const json = {
"results": [
{
category: "category1",
name: "Fred"
},
{
category: "category1",
name: "Teddy"
},
{
category: "category2",
name: "Gilbert"
},
{
category: "category3",
name: "Foxy"
},
]
}
const grouped = json.results.reduce((acc, cur) => {
if (!acc.hasOwnProperty(cur.category)) {
acc[cur.category] = []
}
acc[cur.category].push(cur)
return acc;
}, { })
// parent object now has 3 properties, namely category1, category2 and category3
console.log(JSON.stringify(grouped, null, 4))
// each of these properties is an array of bjects of same category
console.log(JSON.stringify(grouped.category1, null, 4))
console.log(JSON.stringify(grouped.category2, null, 4))
console.log(JSON.stringify(grouped.category3, null, 4))
Note that this json has 4 objects in result array, 2 of cat1, and 1 of cat 2 and cat3. You can run this code in a separate file to see how it works. Ofcourse you will be fetching the json object from server. I just set it for demonstration.
Then set teh state:
this.setState({ grouped })
Then in render() you only show the categories that have items like:
const menuBarButtons = Object.keys(this.state.grouped).map((category) => {
/* your jsx here */
return <MenuItem text={category} key={category} onClick={this.onClick} blah={blah}/>
/* or something , it's up to you */
})
I'm assuming you're showing the items based on the currently selected category this.state.selected. So after you have rendered your menu, you would do something like:
const selectedCatItems = this.state.grouped[this.state.selected].map((item) => {
return <YourItem name={item.name} key={item.id} blah={blah} />
})
Then render it:
return (
<div className="app">
<MenuBar blah={blah}>
{menuBarButtons}
</Menubar>
<div for your item showing area>
{selectedCatItems}
</div>
</div>
)
Also, don't forget to change your onClick() so that it sets this.state.selected state properly. I believe you can figure that out yourself.
Hope it helps.
PS: I didn't write a whole copy/paste solution to your problem simply because I'm reluctant to read and understand your UI details and the whole component to component data passing details..

In ReactJS, how can I best organize my code when iterating over non-shallow data structures

I'm using React and I have the following data structure:
[
{
key: 'test'
data: [
{
id: 1,
name: 'test name',
desc: 'aaaaaaaaa'
},
{
id: 2,
name: 'test name2',
desc: 'aaaaaaaaa'
},
{
id: 3,
name: 'test name3',
desc: 'aaaaaaaaa'
}
]
},
{
key: 'test2'
data: [
{
id: 5,
name: 'test name5',
desc: 'aaaaaaaaa'
},
{
id: 5,
name: 'test name2',
desc: 'aaaaaaaaa'
},
{
id: 6,
name: 'test name6',
desc: 'aaaaaaaaa'
}
]
}
]
I'm looking for a better way of fetching data from such a structure. The thing is that is should be one ul list with li items inside.
I have such a structure for this:
<div className='wrapper'}>
{tabInfo.map(({key, data}) => {
return (
<div className='tab-list' key={key}>
<h4>{key}</h4>
<ul>
{data.map(({id, name, desc}) => {
return (
<li key={id}>
<span className='name'>
{name}
</span>
<span className="desc">{desc}</span>
</li>
)
})}
</ul>
</div>
)
})}
</div>
What is the better way of fetching the data in such a case? I think map inside map isn't good idea.
I believe this will do it, though I haven't tested this to be sure. You can move this to a separate component which can then be used as part of the map:
const ListItem = ({ id, name, desc }) => (
<li key={id}>
<span className='name'>
{name}
</span>
<span className="desc">
{desc}
</span>
</li>
);
<div className='wrapper'}>
{tabInfo.map(({key, data}) => {
return (
<div className='tab-list' key={key}>
<h4>{key}</h4>
<ul>
{data.map(dataItem => <ListItem {...dataItem} />)}
</ul>
</div>
)
})}
</div>
I may have missed something here, so if there are any problems, I'd be happy to know about the,m so I can fix this up

Categories