ReactJS: Map array of objects - javascript

I am trying to map array of objects [{thing: '', quantity: ''}] with ReactJS and this is the code i use for it (and it does not work).
EDIT this is the original code
<ul>
{this.state.ingredients.map(({i,q},k)=>{
return (
<div key={k}>
<li>{i} - {q}</li><br/>
</div>
)
})}
</ul>
what is the reason?

Pass the properties to your map. When destructuring objects, you need to provide the exact keys.
for example, doing the following returns undefined, since obj doesn't have the given props. Object_destructuring
const obj = {a: 'a', b: 'b'}
const { c, d} = obj;
console.log(c, d);
{this.state.things.map(({ thing, quantity }, k) => {
return (
<div key={k}>
<li key={k}>{thing} - {quantity}</li><br />
</div>
)
})}
DEMO
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.21.1/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React',
things: [{ thing: 'Thing ', quantity: 23 }],
};
}
render() {
return (
<div>
{this.state.things.map(({ thing, quantity }, k) => {
return (
<div key={k}>
<li key={k}>{thing} - {quantity}</li><br />
</div>
)
})}
</div>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
</script>

Probably you are looking for something like this, you are not destructuring correctly.
const obj = {thing:'something',quantity:1};
const arr = [obj];
{
arr.map(({ thing, quantity }, index) => {
return (
<div key={index}>
<li>{thing} - {quantity}</li><br/>
</div>
)
})}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Related

accepting parameters with component as a function in react (quick fix)

I have the code below:
files.map((fl) => <AudioFileListItem key={fl.id} id={fl.id} name={fl.name} />)
I call this component in a different file called Test.js which is also a component. I am trying to pass these variables into the component. I was wondering how I could modify AudioFileListItem to accept these variables.
Here is the component rn:
function AudioFileListItem() {
return (
<div className="AudioFileListItem">
</div>
);
}
export default AudioFileListItem;
you need to use props to pass data across components. checkout the following code
ReactDOM.render(<App />, document.getElementById("root"));
function App() {
const files = [
{ id: 1, name: "orange" },
{ id: 2, name: "banana" },
{ id: 3, name: "coconut" },
];
return (
<div className="App">
<div className={"bg-secondary min-vh-100"}>
{files.map(fl => (
<AudioFileListItem key={fl.id} data={fl} />
))}
</div>
</div>
);
}
function AudioFileListItem(props) {
return <div className="AudioFileListItem">{props.data.name}</div>;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root">123</div>

How can I grab the key of a list item generated from a map function?

So I am learning React, and I've tried searching for solutions to my problem both on stackoverflow and on React's own documentation, but I am still stumped.
Essentially, I have a list of 10 subreddits that is being mapped to list items in the form of the subredditsArray variable.
I render the results, and try to pass the selected item when I click that list item to my getSubredditInfo function. However, this doesn't work - event.target.key is undefined. (To clarify, I am looking to grab the key of the single list element that I have clicked).
When I try to just get event.target, I get the actual htmlElement (ex: <li>Dota2</li>), where as I want to get the key, or at least this value into a string somehow without the tags. I also tried putting my onClick method in the list tag of the map function, but that did not work.
Here is the relevant code:
//this is where I get my data
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(event){
//console.log(event.target.key); <-- DOESNT WORK
}
render() {
var subredditsArray = this.state.subreddits.map(function(subreddit){
return (<li key={subreddit.toString()}>{subreddit}</li>);
});
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul onClick={this.getSubredditInfo}>{subredditsArray}</ul>
</div>
);
}
My questions essentially boil down to:
How do I grab the key value from my list object?
Additionally, is there a better way to generate the list than I currently am?
Thank you in advance.
EDIT: Added my componentDidMount function in hopes it clarifies things a bit more.
try the following code:
class App extends React.Component {
constructor(props){
super(props);
this.state = {subreddits:[]};
}
componentDidMount(){
fetch('https://www.reddit.com/api/search_reddit_names.json?query=dota2')
.then(results => {
return results.json();
})
.then(redditNames => {
//this is there I set my subreddits state variable to the array of strings
this.setState({subreddits: redditNames.names});
})
}
getSubredditInfo(subreddit){
console.log(subreddit);
}
render() {
return <div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>
{
this.state.subreddits.map((subreddit)=>{
return (<li key={subreddit.toString()} onClick={()=>this.getSubredditInfo(subreddit)}>{subreddit}</li>);
})
}
</ul>
</div>;
}
}
ReactDOM.render(
<App/>,
document.getElementById('container')
);
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>
please check the onClick event handler now. its an arrow function and its calling the getSubredditInfo function with your subreddit now. so you will get it there.
so its basically different way of calling the handler to pass data to the handler.
it works as you expect it to.
You can use lamda function or make component for item list which have own value for getSubredditInfo function
getSubredditInfo(value) {}
render() {
var subredditsArray = this.state
.subreddits.map((subreddit, i) =>
(<li key={i}
onClick={() => this.getSubredditInfo(subreddit)}>{subreddit}</li>));
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
1) Key should be grabbed either by the id in your object in array. Or you can combine the 2 properties to create a unique key for react to handle re-renders in a better way.
If you have a string array, you may use a combination of string value + index to create a unique value, although using index is not encouraged.
Given a quick example for both below.
2) A better way could be to move your map function into another function and call that function in render function, which will return the required JSX. It will clean your render function.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
subredditsObjArray: [
{ id: 1, value: 'A'},
{ id: 2, value: 'B'},
{ id: 3, value: 'C'},
{ id: 4, value: 'D'}
],
subredditsArray: ['A', 'B', 'C', 'D'],
selectedValue: ''
};
}
getSubredditInfo = (subreddit) => {
console.log(subreddit)
this.setState({
selectedValue: ((subreddit && subreddit.id) ? subreddit.value : subreddit),
});
}
render() {
return (
<div className="redditResults">
<p>Selected Value: {this.state.selectedValue}</p>
<h1>Top {this.state.subredditsArray.length || '0'} subreddits for that topic</h1>
<p>With Objects Array</p>
<ul>
{
this.state.subredditsObjArray
&& this.state.subredditsObjArray.map(redditObj => {
return (<li key={redditObj.id}><button onClick={() => this.getSubredditInfo(redditObj)}>{redditObj.value || 'Not Found'}</button></li>);
})
}
</ul>
<br />
<p>With Strings Array</p>
<ul>
{
this.state.subredditsArray
&& this.state.subredditsArray.map((reddit, index) => {
return (<li key={reddit + '-' + index}><button onClick={() => this.getSubredditInfo(reddit)}>{reddit || 'Not Found'}</button></li>);
})
}
</ul>
</div>
);
}
}
ReactDOM.render(
<App etext="Edit" stext="Save" />,
document.getElementById('container')
);
<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="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Are you trying to do this? I'm not sure what you want to do.
getSubredditInfo(e, subreddit) {
console.log(subreddit)
}
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
this.getSubredditInfo(e, subreddit)
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}
The key purpose is to pass your subreddit to the onClick function so you will receive the value while you click the item.
If you still get error try this and tell me what's happened.
render() {
const { subreddits } = this.state
var subredditsArray = subreddits.map(subreddit => (
<li
key={subreddit.toString()}
onClick={(e) => {
console.log(subreddit.toString())
}}
>
{subreddit}
</li>
))
return (
<div className="redditResults">
<h1>Top 10 subreddits for that topic</h1>
<ul>{subredditsArray}</ul>
</div>
);
}

Updating attributes with search bar in react

I am trying to dynamically render and update views when string contains substring with addition of attribute.
I need to use JavaScript object like this: var ObjectArray =[{"title:"Great Title"},{"title":"Interesting Title"},{"title":"Boring Title"}];
This is what i found but it uses react kind of object and renders only fitting matches.
let contacts = [{
id: 1,
name: 'Sherlock',
phone: '221 221 221'
}, {
id: 2,
name: 'Watson',
phone: '332 333 331'
}]
class App extends React.Component {
render() {
return (
<div>
<h2>Contact List</h2>
<br />
<ContactList contacts={this.props.contacts} />
</div>
)
}
}
class Contact extends React.Component {
render() {
return (
<li>{this.props.contact.name} {this.props.contact.phone}</li>
)
}
}
class ContactList extends React.Component {
constructor() {
super();
this.state = {
search: ''
};
}
updateSearch(event) {
this.setState({
search: event.target.value.substr(0, 10)
});
}
render() {
let filteredContacts = this.props.contacts.filter(
(contact) => {
return contact.name.toLowerCase().includes(this.state.search.toLowerCase());
}
);
console.log(filteredContacts);
return (
<div>
<input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
<hr />
<ul>
{filteredContacts.map((contact) => {
return <Contact contact={contact} key={contact.id} />
})}
</ul>
</div>
)
}
}
ReactDOM.render(<App contacts={contacts} />, document.getElementById('container'));
ReactDOM.render(
<FilterableProductTable products={PRODUCTS} />,
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>
<body>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
I had my attempts at changing this code but it used all static solutions which did not update.
This is a concept i had:
var ObjectArray =[{"title":"Great Title"},{"title":"Interesting Title"},{"title":"Boring Title"}];
var BoolArray = Array(ObjectArray.length).fill(false);
class App extends React.Component {
constructor() {
super();
this.state = {
search: ''
};
}
updateSearch(event) {
this.setState({
search: event.target.value.substr(0, 10)
});
}
render() {
return(
<input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
)
{ObjectArray.map((obj, index) => {
return(
<h1 className={BoolArray[index] ? 'red' : 'blue'}>{obj.title}</h1>
)
}
)
}
}
ReactDOM.render(
<App/>
document.getElementById('root')
);
.red{
color : red;
}
.blue{
color: blue
}
<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">
yes is expected to be an object map but is not defined, that will cause issues.
return(
<input className="text" type="text" value={this.state.search} onChange={this.updateSearch.bind(this)} />
)
so, you return that expression and then continue under it? that's dead code because function has exited.
react can only currently return 1 root level element. if you need to return more, you have to wrap into an element.
so
render(){
return <div>
<input ... />
{something.map(el => <h1>{el.title}</h1>)}
</div>
}

How to render an array of objects in React?

could you please tell me how to render a list in react js.
I do like this
https://plnkr.co/edit/X9Ov5roJtTSk9YhqYUdp?p=preview
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
hello
</div>
);
}
}
You can do it in two ways:
First:
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>);
return (
<div>
{listItems }
</div>
);
}
Second: Directly write the map function in the return
render() {
const data =[{"name":"test1"},{"name":"test2"}];
return (
<div>
{data.map(function(d, idx){
return (<li key={idx}>{d.name}</li>)
})}
</div>
);
}
https://facebook.github.io/react/docs/jsx-in-depth.html#javascript-expressions
You can pass any JavaScript expression as children, by enclosing it within {}. For example, these expressions are equivalent:
<MyComponent>foo</MyComponent>
<MyComponent>{'foo'}</MyComponent>
This is often useful for rendering a list of JSX expressions of arbitrary length. For example, this renders an HTML list:
function Item(props) {
return <li>{props.message}</li>;
}
function TodoList() {
const todos = ['finish doc', 'submit pr', 'nag dan to review'];
return (
<ul>
{todos.map((message) => <Item key={message} message={message} />)}
</ul>
);
}
class First extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [{name: 'bob'}, {name: 'chris'}],
};
}
render() {
return (
<ul>
{this.state.data.map(d => <li key={d.name}>{d.name}</li>)}
</ul>
);
}
}
ReactDOM.render(
<First />,
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>
Shubham's answer explains very well. This answer is addition to it as per to avoid some pitfalls and refactoring to a more readable syntax
Pitfall : There is common misconception in rendering array of objects especially if there is an update or delete action performed on data. Use case would be like deleting an item from table row. Sometimes when row which is expected to be deleted, does not get deleted and instead other row gets deleted.
To avoid this, use key prop in root element which is looped over in JSX tree of .map(). Also adding React's Fragment will avoid adding another element in between of ul and li when rendered via calling method.
state = {
userData: [
{ id: '1', name: 'Joe', user_type: 'Developer' },
{ id: '2', name: 'Hill', user_type: 'Designer' }
]
};
deleteUser = id => {
// delete operation to remove item
};
renderItems = () => {
const data = this.state.userData;
const mapRows = data.map((item, index) => (
<Fragment key={item.id}>
<li>
{/* Passing unique value to 'key' prop, eases process for virtual DOM to remove specific element and update HTML tree */}
<span>Name : {item.name}</span>
<span>User Type: {item.user_type}</span>
<button onClick={() => this.deleteUser(item.id)}>
Delete User
</button>
</li>
</Fragment>
));
return mapRows;
};
render() {
return <ul>{this.renderItems()}</ul>;
}
Important : Decision to use which value should we pass to key prop also matters as common way is to use index parameter provided by .map().
TLDR; But there's a drawback to it and avoid it as much as possible and use any unique id from data which is being iterated such as item.id. There's a good article on this - https://medium.com/#robinpokorny/index-as-a-key-is-an-anti-pattern-e0349aece318
Try this below code in app.js file, easy to understand
function List({}) {
var nameList = [
{ id: "01", firstname: "Rahul", lastname: "Gulati" },
{ id: "02", firstname: "Ronak", lastname: "Gupta" },
{ id: "03", firstname: "Vaishali", lastname: "Kohli" },
{ id: "04", firstname: "Peter", lastname: "Sharma" }
];
const itemList = nameList.map((item) => (
<li>
{item.firstname} {item.lastname}
</li>
));
return (
<div>
<ol style={{ listStyleType: "none" }}>{itemList}</ol>
</div>
);
}
export default function App() {
return (
<div className="App">
<List />
</div>
);
}
import React from 'react';
class RentalHome extends React.Component{
constructor(){
super();
this.state = {
rentals:[{
_id: 1,
title: "Nice Shahghouse Biryani",
city: "Hyderabad",
category: "condo",
image: "http://via.placeholder.com/350x250",
numOfRooms: 4,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 43
},
{
_id: 2,
title: "Modern apartment in center",
city: "Bangalore",
category: "apartment",
image: "http://via.placeholder.com/350x250",
numOfRooms: 1,
shared: false,
description: "Very nice apartment in center of the city.",
dailyPrice: 11
},
{
_id: 3,
title: "Old house in nature",
city: "Patna",
category: "house",
image: "http://via.placeholder.com/350x250",
numOfRooms: 5,
shared: true,
description: "Very nice apartment in center of the city.",
dailyPrice: 23
}]
}
}
render(){
const {rentals} = this.state;
return(
<div className="card-list">
<div className="container">
<h1 className="page-title">Your Home All Around the World</h1>
<div className="row">
{
rentals.map((rental)=>{
return(
<div key={rental._id} className="col-md-3">
<div className="card bwm-card">
<img
className="card-img-top"
src={rental.image}
alt={rental.title} />
<div className="card-body">
<h6 className="card-subtitle mb-0 text-muted">
{rental.shared} {rental.category} {rental.city}
</h6>
<h5 className="card-title big-font">
{rental.title}
</h5>
<p className="card-text">
${rental.dailyPrice} per Night ยท Free Cancelation
</p>
</div>
</div>
</div>
)
})
}
</div>
</div>
</div>
)
}
}
export default RentalHome;
Try this:
class First extends React.Component {
constructor (props){
super(props);
}
render() {
const data =[{"name":"test1"},{"name":"test2"}];
const listItems = data.map((d) => <li key={d.name}>{d.name}</li>;
return (
<div>
{listItems}
</div>
);
}
}

Use map to create HTML ReactJS

I have an array of objects.
notifications = [
{notification:"this is notification1"},
{notification:"this is notification2"},
{notification:"this is notification3"},
]
Ive been trying to map through the array and create HTML code out of it.
return (
<div>
{notifications.map(function(notificationItem) {
<a> {notificationItem.notification} </a>
})}
</div>
);
Can somebody please tell me what is the mistake in this?
Thank you!
From .map you should return value - add return statement to .map., also in this case you should add key property for each element., because child elements should have unique keys., you can read more about reconciliation here
var App = React.createClass({
render: function() {
const notifications = this.props.notifications
.map(function(notificationItem, index) {
return <a key={index}> {notificationItem.notification} </a>;
});
return <div>{ notifications }</div>;
}
});
var notifications = [
{notification:"this is notification1"},
{notification:"this is notification2"},
{notification:"this is notification3"},
];
ReactDOM.render(
<App notifications={ notifications } />,
document.getElementById('container')
);
<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="container"></div>
I'd also add that if you don't need stateful React, you could also write your component in this style:
const notifications = [
{ notification: "1" },
{ notification: "2" },
{ notification: "3" },
];
const App = function({ notifications }) {
return (
<div>
{
notifications.map((item, index) => <a key={index}>{item.notification}</a>)
}
</div>
)
}
ReactDOM.render(
<App notifications={ notifications } />,
document.getElementById("app")
)

Categories

Resources