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>
);
}
}
Related
I have an app with a drop menu in the Navbar component that contains clothing sizes (S,M,L). All the clothing is being rendered in the parent App component from json. I want to re-render the section of the App component that displays the items with items from the json based on the menu selection. Any tips appreciated.
I'm rendering everything from json initially and updating state when the drop menu changes but can't re-render the products in the parent component (displayed in grid)
JSON
const tops = [{
index: 0,
isSale: true,
isExclusive: false,
price: "$18.88",
productImage: "product-1.jpg",
productName: "Striped top",
size: ["XS", "S", "L", "XL"]
},
{
index: 1,
isSale: false,
isExclusive: false,
price: "$25.44",
productImage: "product-2.jpg",
productName: "Denim top",
size: ["XS", "S"]
},
{
index: 2,
isSale: false,
isExclusive: true,
price: "$12.93",
productImage: "product-3.jpg",
productName: "Plain cotton top",
size: ["S", "M"]
...
}];
Top, Navbar and App components
let saleExclusive = null;
class Top extends Component {
constructor(props) {
super(props);
this.state = {
id: null,
productName: null,
productImage: null,
isSale: null,
isExclusive: null,
size: null,
price: null,
saleExclusive: null
}
}
render (){
if(this.props.isSale===true){
saleExclusive = <div className="btn btn-danger m20">Sale</div>
}
else if(this.props.isExclusive===true){
saleExclusive = <div className="btn btn-success m20">Exclusive</div>
}
else {
saleExclusive = <div className="blank"></div>
}
return (
<div className="col-3 displayBox">
<div className="row text-center"><img src={require("./assets/images/"+this.props.productImage)} width="90%" height="90%" alt="Select this" /></div>
<div className="row">
{ saleExclusive }
</div>
<div className="row h50">
<div className="col-7 text-left prod-desc">{this.props.productName}</div>
<div className="col-3 prod-price">{this.props.price}</div>
</div>
</div>
);
}
}
class NavBar extends Component {
constructor(props) {
super(props);
this.state = {
selectValue: '',
selectedSize: '',
data:''
}
}
componentDidUpdate(){
console.log(this.state.selectedSize)
}
render (){
var handleChange = this.props.handleChange;
return( <div className="row navbarpoz">
<div className="col-6 text-left heading">
{this.props.navTitle}
</div>
<div className="col-6 text-right">
<select id="availablesizes"
defaultValue={this.selectValue}
onChange={() => handleChange(this.value)}>
<option>Filter by size</option>
<option value="XS">XS</option>
<option value="S">S</option>
<option value="M">M</option>
<option value="L">L</option>
<option value="XL">XL</option>
</select>
</div>
</div>
);
}
}
class App extends Component {
constructor(props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: "...let's shop",
selectedSize: ''
}
}
handleChange(size) {
this.setState({
selectedSize: size
})
alert(size);
}
getData(){
setTimeout(() => {
this.setState({
data: "Women's tops",
selectedSize: 'S'
})
}, 1000)
}
componentDidMount(){
this.getData();
}
render() {
return (
<div className="container">
{this.state.selectedSize}
<NavBar navTitle={this.state.data}/>
<div id="itemGrid" className="text-center">
{tops.map(tops => (
<Top handleChange={this.handleChange}
key={tops.id}
productName={tops.productName}
productImage={tops.productImage}
isSale={tops.isSale}
isExclusive={tops.isExclusive}
size={tops.size}
price={tops.price}
/>
))}
</div>
</div>
);
}
}
export default App
When user selects XS from the menu then only the json with size: ["XS"] needs to display in the grid. At present the parent isn't getting updated.
In your <select /> in Navbar you're using this.value but this here refers to the Navbar component which doesn't set any value so it's always null.
onChange={() => handleChange(this.value)}>
What you want to do is this :
onChange={event => handleChange(event.taget.value)}>
Then in your App you'll need to filter tops with the selectedSize state :
const suitableTops = tops.filter(top => top.size.includes(this.state.selectedSize));
Before mapping it like you already do in render:
suitableTops.map(top => <Top key={top.id} {...top} />);
But the result will be empty before the user selects a size because this.state.selectedSize is default to '', so you may want to set an existing size by default to your selectedSize state. Or wrap the filter in a condition.
I'm trying to add and remove product when clicking a button, and each button is in different component and the data that I'm getting from is in storeData component where inside there is an object with a true/false status if the status is true the product should display in Cart component if false it will remove the product.
now in ProductList component when I click the add button the status is changing to true, but it's not changing the actual status in storeData component so the result when i go to Cart component nothing is displayed
I know I'm doing this the wrong way, so how can I perform this add and remove operation, i'm new in React.js so please any help would really be appreciated.
ProductList component
import itemlist from "../storeData/storeData";
import { Link } from "react-router-dom";
class ProductList extends Component {
state = {
items: itemlist.items,
addToCart: null
};
addItem(id) {
let itemArray = [];
itemlist.cartItems.filter(target => {
return id === target.id ? itemArray.push(target) : null;
});
const addToCart = itemArray[0];
addToCart.status = false;
this.setState({ addToCart });
}
render() {
return (
<div className="list-wrap">
{this.state.items.map(item => {
return (
<div key={item.id}>
<Link to={{ pathname: "/productdetail", itemdetail: item }}>
<img alt="item img" src={item.posterUrl} />
</Link>
<h2>{item.title}</h2>
<h3>${item.price}</h3>
<button onClick={() => this.addItem(item.id)}>Add to Cart</button>
</div>
);
})}
</div>
);
}
}
export default ProductList;
Cart component
import itemlist from "../storeData/storeData";
class Cart extends Component {
state = {
cart: itemlist.cartItems,
remove: null
};
removeItem(id) {
let itemArray = [];
itemlist.cartItems.filter(target => {
return id === target.id ? itemArray.push(target) : null;
});
let remove = itemArray[0];
remove.status = false;
this.setState({ remove });
}
render() {
return (
<div>
{this.state.cart.map(itm => {
return itm.status === false ? null : (
<div key={itm.id} className="cart-layout">
<img alt="img" src={itm.posterUrl} />
<h4>{itm.title}</h4>
<h4>{itm.price}</h4>
<button onClick={() => this.removeItem(itm.id)}>Remove</button>
</div>
);
})}
</div>
);
}
}
storeData component
let itemlist = {
items: [
{
id: 1,
title: "name 1",
price: "232",
posterUrl:
"https://images-na.ssl-images-amazon.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE#._V1_SX300.jpg"
},
{
id: 2,
title: "name 2",
price: "65",
posterUrl:
"https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NTc2NjYwOV5BMl5BanBnXkFtZTcwMzk5OTY0MQ##._V1_SX300.jpg"
},
],
cartItems: [
{
id: 1,
status: false,
title: "name 1",
price: "232",
posterUrl:
"https://images-na.ssl-images-amazon.com/images/M/MV5BMjIxNTU4MzY4MF5BMl5BanBnXkFtZTgwMzM4ODI3MjE#._V1_SX300.jpg"
},
{
id: 2,
status: false,
title: "name 2",
price: "65",
posterUrl:
"https://images-na.ssl-images-amazon.com/images/M/MV5BMTY5NTc2NjYwOV5BMl5BanBnXkFtZTcwMzk5OTY0MQ##._V1_SX300.jpg"
},
]
};
I don't think you are using filter correctly here, in either component. You are confusing the filter test with the action of composing your array. All you need with the filter is a test that will return a boolean and that will construct the array for you.
Try changing:
let itemArray = [];
itemlist.cartItems.filter(target => {
return id === target.id ? itemArray.push(target) : null;
});
To
const itemArray = itemlist.cartItems.filter(target => id === target.id);
And similarly in the cart component.
For more detail on filter see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
I'm new to reactjs and trying to build a basic ui by parsing a json object.
class App extends Component {
state ={
products : []
};
render() {
return (
<div>
<Form onSubmit = {this.addNewProducts}/>
<ProductList products = {this.state.products} />
</div>
);
}
}
const ProductList =(props) => {
return(
<div>
{props.products.map(product => <Product {...product}/>)}
</div>
);
}
Trying to render some of the data from json here, which is where I'm struggling with. Tried to use Map but no luck, at least I'm not using it correctly.
const Product = (props) => {
return(
<div>
<div>
{props.pludetails}</div>
</div>
);
};
Returning JSON object:
{
"ResultCode": 0,
"ResultDescription": "Success",
"pludetails": [
{
"UPC": 490000129,
"ItemDesc": "SPRITE",
"Department": 197,
"Price1": 7.99,
}
]
}
Now you just render js object as React child, but you need to write render function for pludetails.
Here is short example:
const Pludetails = (props) => {
return(
<div>
<span>{props.UPC}</span>
<span>{props.ItemDesc}</span>
<span>{props.Department}</span>
<span>{props.Price1}</span>
</div>
);
}
In Product :
const Product = (props) => {
return(
<div>
<div>
{props.pludetails.map((item, i) => <Pludetails key={i} {...item}/>)}
</div>
</div>
);
};
In ProductList add key prop, for reduce key errors:
const ProductList =(props) => {
return(
<div>
{props.products.map((product, i) => <Product key={i} {...product}/>)}
</div>
);
}
Use indexes by keys width dynamic data is danger. You can use id or something else what you wont.
I just started learing react and I run in a trouble trying to update state of a single <Option /> child Element.
My flux Store is emiting change and in React devtools I can see the state of StyleOptions element being updated but it doesn't update the child components <Option />.
I suspect this is because I got the list of options kept in a variable.
I need to use this because I'm pulling this options from JSON.
const Options = this.state.options.map((parent) => {
const children = parent.children.map((child) => {
return (
<Option {...child} />
)
});
return <Option {...parent} children={children} />;
});
So I think this part might be causing problems.
My example data from OptionsStore looks like this.
this.options = [
{
key: "suitType",
label: "Suit Type",
selected: false,
children: [
{
key: "suittype_skinny",
parent: "suitType",
label: "Skinny",
price: "£50",
description: "Short description",
images: {
general: "http://placehold.it/600x600",
closeUp: "http://placehold.it/620x620",
thumbnail: "http://placehold.it/100x100",
},
selected: false,
},
{
key: "suittype_wedding",
parent: "suitType",
label: "Wedding",
price: "£50",
description: "Short description",
images: {
general: "http://placehold.it/600x600",
closeUp: "http://placehold.it/620x620",
thumbnail: "http://placehold.it/100x100",
},
selected: false,
}
]
}
]
Also the child props aren't being changed.
Full code here:
import React, { Component } from 'react';
import Option from './Option';
import OptionsStore from '../../stores/OptionsStore';
class StyleOptions extends Component {
constructor(props) {
super(props)
this.state = {
options: OptionsStore.getAllItems(),
}
}
componentDidMount() {
OptionsStore.on('change',(e) => {
this.setState({
options: OptionsStore.getAllItems(),
});
console.log('optionsStore received an update');
});
}
render() {
const Options = this.state.options.map((parent) => {
const children = parent.children.map((child) => {
return (
<Option {...child} />
)
});
return <Option {...parent} children={children} />;
});
return(
<div className="col-xs-6">
<ul className="list-group">
{Options}
</ul>
</div>
)
}
}
export default StyleOptions;
also the <Option /> code:
import React, { Component } from 'react';
export default class Option extends Component {
constructor(props) {
super(props);
this.hasChildren = this.props.children ? true : false;
this.hasThumb = this.props.images ? true : false;
this.children = this.state.children;
this.state = {
label: this.props.label,
description: this.props.description,
selected: false,
price: this.props.price
}
}
render() {
return (
<li className={this.hasChildren ? 'list-group-item':'col-sm-4 list-group-item' } selected={this.state.selected}>
<a className="media">
{this.hasThumb ? (
<div className="media-left media-middle">
<img src={this.props.images.thumbnail} alt={this.state.label} />
</div>
) : (
' '
)}
<div className="media-body">
<h4 className="option-name">{this.state.label}</h4>
<p className="info">{this.state.description}</p>
<span className="text-success pricing">{this.state.price}</span>
</div>
</a>
{this.hasChildren ? (
<ul className="panel-body">
{this.children}
</ul>
) : (
' '
)}
</li>
)
}
}
I hope anyone could help.
The issue is inside of your Option component.
You define this.children = this.state.children . After that, you define your initial state but there is no "children". So that children state is notdefined.
First, add children: this.props.children into your state.
Then, change
{this.hasChildren ? (
<ul className="panel-body">
{this.children}
</ul>
) : (
' '
)}
to
{this.hasChildren ? (
<ul className="panel-body">
{this.state.children}
</ul>
) : (
' '
)}
and there is no need to define this.children = this.state.children.
I hope it solves the issue.
Thank you alireza for your help.
I managed to fix it. The problem was that the <Option /> was receiving too much info. I removed all state calls and left only the if statements like below.
import React, { Component } from 'react';
export default class Option extends Component {
constructor(props) {
super(props);
this.hasChildren = this.props.children ? true : false;
this.hasThumb = this.props.images ? true : false;
//this.state = this.props;
}
render() {
return (
<li className={this.hasChildren ? 'list-group-item':'col-sm-4 list-group-item' }>
<a className="media">
{this.hasThumb ? (
<div className="media-left media-middle">
<img src={this.props.images.thumbnail} alt={this.props.label} />
</div>
) : (
' '
)}
<div className="media-body">
<h4 className="option-name">{this.props.label}</h4>
<p className="info">{this.props.description}</p>
<span className="text-success pricing">{this.props.price}</span>
</div>
</a>
{this.hasChildren ? (
<ul className="panel-body">
{this.props.children}
</ul>
) : (
' '
)}
</li>
)
}
}
Then modified my stateful component <StyleOptions /> like below
import React, { Component } from 'react';
import Option from './Option';
import OptionsStore from '../../stores/OptionsStore';
class StyleOptions extends Component {
constructor(props) {
super(props)
this.state = {
options: OptionsStore.getAllItems(),
}
}
componentWillMount() {
OptionsStore.on("change", () => {
this.setState({
options: OptionsStore.getAllItems(),
});
console.log('optionsStore received an update');
});
}
render() {
const { options } = this.state;
const allOptions = options.map((option) => {
const { children } = option;
const optionChildren = children.map((child) => {
return <Option {...child} />;
})
return <Option {...option} children={optionChildren} />;
});
return(
<div className="col-xs-12">
<ul className="list-group">
{allOptions}
</ul>
</div>
)
}
}
export default StyleOptions;
Not sure why it is working correctly now. I suspect that It might have changed because I modified the maps a little bit.
Old one / Broken one:
const Options = this.state.options.map((parent) => {
const children = parent.children.map((child) => {
return (
<Option {...child} />
)
});
return <Option {...parent} children={children} />;
});
New one/working:
const { options } = this.state;
const allOptions = options.map((option) => {
const { children } = option;
const optionChildren = children.map((child) => {
return <Option {...child} />;
})
return <Option {...option} children={optionChildren} />;
});
Right now I'm stuck with pagination implementation with React. I have all the neccessary data from JSON, however I got no result.
Here's the code I use:
first, I fetch data from the server:
constructor() {
super();
this.state = {items: {}, totalPages: [], nextPage: []};
}
componentDidMount() {
let url = 'http://localhost:8000/items?json=true';
request.get(url).then((response) => {
this.setState({
items: response.body.items.data,
totalPages: response.body.items.last_page,
nextPage: response.body.items.next_page_url
});
});
}
Thus I get a simple JSON file:
{
"items": {
"total": 26025,
"per_page": 16,
"current_page": 1,
"last_page": 1627,
"next_page_url": "http://localhost:8000/items?page=2",
"prev_page_url": null,
"from": 1,
"to": 16,
"data": [
{
"id": 1,
...
},
{
"id": 2,
...
},
...
]
}
}
I successfully display items data in render method like this:
let items = _.map(this.state.items, (item) => {
return (
<div key={item.id}>
<div className="content">
<span>
{item.type}
</span>
...
</div>
</div>
)
});
and then return it like so:
return (
<div>
{items}
</div>
<div>
<a href={this.state.nextPage}>Next</a>
</div>
)
I can see that URL changes after I press Next button to page2 but there are two issues: I want to change items components based on JSON file when I click Next (i.e first page contains the first set of 16 elements, second page contains the second set) but there is no change and when I click Next button again but on the second page (according to URL) it doesn't get me to the third page and so on.
I know I need to somehow bind these state to page2 URL shows content described on the second page and I ran through tutorials but they seem to be outdated in case I use React 15.2.1.
I would appreciate any help or a thought that'd help me to solve it!
Add a click handler to your link element and pass the url as parameter. In the handler function make the ajax request and update the states using setState (similar to the one u did it on componentDidMount).
constructor() {
super();
this.state = {
items: [],
totalPages: '',
nextPage: ''
};
this._loadData = this._loadData.bind(this);
}
componentDidMount() {
const url = 'http://localhost:8000/items?json=true';
this._loadData(url);
}
_loadData(url) {
request.get(url).then((response) => {
this.setState({
items: response.body.items.data,
totalPages: response.body.items.last_page,
nextPage: response.body.items.next_page_url
});
});
}
render() {
let items = _.map(this.state.items, (item) => {
return (
<div key={item.id}>
<div className="content">
<span>
{item.type}
</span>
...
</div>
</div>
)
});
return (
<div>
{items}
</div>
<div>
<a href="#0" onClick={this._loadData(this.state.nextPage)}>Next</a>
</div>
)
}