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 a class based React component that is using items in state and rendering result. Here is short snippet how I do this:
class Menu extends Component {
constructor(props) {
super(props);
this.state = {
items: props.items.edges,
someItems: props.items.edges,
}
}
render() {
if (this.state.items.length > 0) {
return (
<div className="container">
<div className="row">
{this.state.someItems.map(({ node }) => {
return (
<div key={node.id}>
<div>
render some data
</div>
</div>
)
})}
</div>
</div>
);
}
}
}
The data is received as objects inside an array, like this:
My question is would it be possible to sort these items alphabetically before being rendered? What would be the best approach for this?
The best approach is to sort the items before you set them to the state. You can use the built in Array.prototype.sort method in order to sort the items. You can use the String.prototype.localeCompare in order to compare strings alphabetically.
I don't know the expected structure of your data so here is a general solution.
class App extends React.Component {
constructor(props) {
super(props);
// Make a copy so as not to modify the original array directly
const sortedCopy = [...props.items];
sortedCopy.sort((a, b) => a.name.localeCompare(b.name));
this.state = {
items: sortedCopy,
};
}
render() {
return (
<div>
{this.state.items.map((item) => (
<p key={item.id}>
<div>Item - {item.name}</div>
</p>
))}
</div>
);
}
}
// Example items prop is out of order
const items = [
{ id: 0, name: "C" },
{ id: 1, name: "B" },
{ id: 2, name: "A" },
{ id: 3, name: "D" },
];
ReactDOM.render(<App items={items} />, document.getElementById("root"));
<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"></div>
I try to loop my state with a .map but it didn't work (I don't see my log in the console) ...
{this.state["cards"].map(card => console.log(card.title))}
Please tell me what's the error...
I have my state here (which a see the log in my console) :
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
cards: []
};
fs.readdir(testFolder, (err, files) => {
files.forEach(file => {
this.state.cards.push({
title: "Test1",
pic:
"https://seeklogo.com/images/C/confluence-logo-D9B07137C2-seeklogo.com.png",
content: "Content",
link: "#"
});
});
});
console.log(this.state); // I see this one
}
Here is my console log :
Object {cards: Array[2]}
cards: Array[2]
0: Object
title: "Test1"
pic: "https://seeklogo.com/images/C/confluence-logo-D9B07137C2-seeklogo.com.png"
content: "Content"
link: "#"
1: Object
title: "Test1"
pic: "https://seeklogo.com/images/C/confluence-logo-D9B07137C2-seeklogo.com.png"
content: "Content"
link: "#"
EDIT : Here is my render :
render() {
return (
<div>
<div className="container-fluid text-center">
<h2 className="h2_title">Hi.</h2>
</div>
<div className="main">
<ul className="cards">
{this.state["cards"].map(cards => (
<Card
key={cards.title}
link={cards.link}
title={cards.title}
pic={cards.pic}
/>
))}
</ul>
</div>
</div>
);
}
He doesn't display my cards and if I replace it by console.log it didn't display it in my console
Thanks for helping me !
You need to make your operations inside the componentDidMount and update the state like this:
import React, {Component} from "react";
export default class Home extends Component {
constructor(props) {
super(props);
this.state = {
cards: [],
loading: true
};
}
componentDidMount() {
let cards = [];
fs.readdir(testFolder, (err, files) => {
files.forEach(file => {
cards.push({
title: "Test1",
pic:
"https://seeklogo.com/images/C/confluence-logo-D9B07137C2-seeklogo.com.png",
content: "Content",
link: "#"
});
});
this.setState({
cards,
loading: false
})
});
}
render() {
const {cards, loading} = this.state;
if (loading) {
return (
<div>Getting Files, please wait...</div>
)
}
if (cards.length === 0) {
return (
<div>No files found</div>
)
}
return (
<div>
<div className="container-fluid text-center">
<h2 className="h2_title">Hi.</h2>
</div>
<div className="main">
<ul className="cards">
{cards.map(card => (
<Card
key={card.title}
link={card.link}
title={card.title}
pic={card.pic}
/>
))}
</ul>
</div>
</div>
);
}
}
You shouldn't directly change the state like this this.state.cards.push
I`m changing class after clicking and it works.
The problem is that, classes change simultaneously in both elements and not in each one separately. Maybe someone could look what I'm doing wrong. Any help will be useful.
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
isExpanded: false
};
}
handleToggle(e) {
this.setState({
isExpanded: !this.state.isExpanded
});
}
render() {
const { isExpanded } = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>First</strong>
</p>
</div>
<div
onClick={e => this.handleToggle(e)}
className={isExpanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>Second</strong>
</p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
Every element must have its seperate expanded value. So we need an array in state.
And here is the code:
import React, { Component } from "react";
class PageContentSupportFaq extends Component {
state = {
items: [
{ id: 1, name: "First", expanded: false },
{ id: 2, name: "Second", expanded: true },
{ id: 3, name: "Third", expanded: false }
]
};
handleToggle = id => {
const updatedItems = this.state.items.map(item => {
if (item.id === id) {
return {
...item,
expanded: !item.expanded
};
} else {
return item;
}
});
this.setState({
items: updatedItems
});
};
render() {
return this.state.items.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={el.expanded ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
<span> {el.expanded.toString()}</span>
</p>
</div>
));
}
}
export default PageContentSupportFaq;
You can get two state one state for first and another for a second and handle using two function like this
import React, { Component } from 'react';
class PageContentSupportFaq extends Component {
constructor(props) {
super(props)
this.state = {
isExpanded: false,
isExpanded2:false,
}
}
handleToggle(e){
this.setState({
isExpanded: !this.state.isExpanded
})
}
handleToggle2(e){
this.setState({
isExpanded2: !this.state.isExpanded2
})
}
render() {
const {isExpanded,isExpanded2} = this.state;
return (
<div className="section__support--faq section__full--gray position-relative">
<div className="container section__faq">
<p className="p--thin text-left">FAQ</p>
<h2 className="section__faq--title overflow-hidden pb-4">Title</h2>
<p className="mb-5">Subtitle</p>
<div className="faq__columns">
<div onClick={(e) => this.handleToggle(e)} className={isExpanded ? "active" : "dummy-class"}>
<p className="mb-0"><strong>First</strong></p>
</div>
<div onClick={(e) => this.handleToggle2(e)} className={isExpanded2 ? "active" : "dummy-class"}>
<p className="mb-0"><strong>Second</strong></p>
</div>
</div>
</div>
</div>
);
}
}
export default PageContentSupportFaq;
You'll need to track toggled classes in array, that way it will support arbitrary number of components:
// Save elements data into array for easier rendering
const elements = [{ id: 1, name: "First" }, { id: 2, name: "Second" }];
class PageContentSupportFaq extends Component {
constructor(props) {
super(props);
this.state = {
expanded: []
};
}
handleToggle(id) {
this.setState(state => {
if (state.isExpanded.includes(id)) {
return state.isExpanded.filter(elId => elId !== id);
}
return [...state.expanded, id];
});
}
render() {
return elements.map(el => (
<div
key={el.id}
onClick={() => this.handleToggle(el.id)}
className={this.isExpanded(el.id) ? "active" : "dummy-class"}
>
<p className="mb-0">
<strong>{el.name}</strong>
</p>
</div>
));
}
}
I have 2 buttons and information about div. When I click on one of the buttons, one component should appear in the div info. Where is the error in the withdrawal of the component div info?
import React, { Component } from 'react';
import Donald from '/.Donald';
import John from '/.John';
class Names extends Component {
state = {
array:[
{id:1,component:<Donald/>, name:"Me name Donald"},
{id:2,component:<John/>, name:"My name John"},
],
currComponentId: null
changeComponentName = (idComponent) => {
this.setState({currComponentId:idComponent});
};
render() {
return(
<table>
<tbody>
<tr className="content">
{
this.state.array.map(item=> item.id===this.element.id).component
}
</tr>
<button className="Button">
{
this.state.array.map( (element) => {
return (
<td key={element.id}
className={this.state.currComponentId === element.id ? 'one' : 'two'}
onClick={ () => this.changeComponentName(element.id)}
>{element.name}
</td>
)
})
}
</button>
</tbody>
</table>
)
}
}
export default Names;
You have several problems here, the first being that you are missing the closing curly bracket on your state. this.element.id is also undefined, I assume you are meaning this.state.currComponentId.
Your html is also fairly badly messed up, for example you are inserting multiple <td>s into the content of your button. I also don't see where this.changeComponentName() is defined, so I am assuming you mean this.showComponent()
The primary issue is probably in this.state.array.map(item=> item.id === this.element.id).component, as map() returns an array. An array.find() would be more appropriate, though you still need to check to see if there is a match.
I might re-write your component like this (I have swapped out the confusing html for basic divs, as I'm not sure what you are going for here)
class Names extends Component {
state = {
array: [
{ id: 1, component: <span>Donald</span>, name: "Me name Donald" },
{ id: 2, component: <span>John</span>, name: "My name John" },
],
currComponentId: null,
};
showComponent = (idComponent) => {
this.setState({ currComponentId: idComponent });
};
render() {
//Finding the selected element
const selectedElement = this.state.array.find(
(item) => item.id === this.state.currComponentId
);
return (
<div>
<div className="content">
{
//Check to see if there is a selected element before trying to get it's component
selectedElement ? selectedElement.component : "no selected."
}
</div>
{this.state.array.map((element) => {
return (
<button
className="Button"
key={element.id}
className={
this.state.currComponentId === element.id ? "one" : "two"
}
onClick={() => this.showComponent(element.id)}
>
{element.name}
</button>
);
})}
</div>
);
}
}
Errors:- (1) You are showing list inside tag, instead show as <ul><li><button/></li></ul>(2)You are not displaying content after comparison in map()This is a working solution of your question.
class Names extends React.Component {
state = {
array: [
{ id: 1, component: <Donald />, name: "Me name Donald" },
{ id: 2, component: <John />, name: "My name John" }
],
currComponentId: null
};
clickHandler = idComponent => {
this.setState({ currComponentId: idComponent });
};
render() {
return (
<div>
<ul>
{this.state.array.map(element => {
return (
<li key={element.id}>
<button
className="Button"
onClick={() => this.clickHandler(element.id)}
>
{element.name}
</button>
</li>
);
})}
</ul>
{this.state.array.map(data => {
if (this.state.currComponentId === data.id)
return <div>{data.component}</div>;
})}
</div>
);
}
}
const Donald = () => <div>This is Donald Component</div>;
const John = () => <div>This is John Component</div>;
ReactDOM.render(<Names />, document.getElementById('root'));
<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' />
I am wondering how to re order a list element. Its like you have a list of an elements li and put the last element in the first place like the index of 10th would be placed in the index of 0
React.render( <div>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li> //When an event fires, this item would go up to the first index </div>, document.getElementById('example') );
Based on your comment on Abdennour answer (you need to update an item regardless of its position), you cannot do such operation with an array of simple numbers, you need to index your values:
class MyList extends Component {
render() {
return(
<ul>
{this.props.items.map((item ,key) =>
<li key={key}> {item}</li>
)}
</ul>
)
}
}
class App extends React.Component{
constructor(props) {
this.state= {
listItems: [{id: 1, val: '1'}, {id: 2, val: '2'}, {id: 2, val: '2'}, {id: 3, val: '3'}]
};
}
reverse = () => {
this.setState({
listItems: this.state.listItems.reverse()
});
}
// You can ignore this, simple put some random value somewhere
// In your case this would be the function that changes the value of one of the items, should of course be NOT random
changeRandom = () => {
const index = Math.floor(Math.random() * this.state.listItems.length);
const newList = this.state.listItems.slice();
newList[index] = Math.floor(Math.random() * 10).toString();
this.setState({
listItems: newList
})
}
render() {
return (
<div>
<div>
<MyList items={this.state.listItems.map(item => item.val)} />
</div>
<div>
<button onClick={reverse}>Reverse</button>
</div>
<div>
<button onClick={changeRandom}>Random Change</button>
</div>
</div>
)
}
}
So, i assume you have two React components: one for the list, and the other is the main component (App) which includes the list as well as the button of reversing the list.
class MyList extends React.Component {
render() {
return(
<ul>
{this.props.items.map((item ,key) =>
<li key={key}> {item}</li>
)}
</ul>
)
}
}
MyList.defaultProps= {items:[]};
class App extends React.Component{
state= {
listItems: ['1', '2', '3', '4']
};
onClick(e) {
e.preventDefault();
this.setState({
listItems: this.state.listItems.reverse()
});
}
render() {
return (
<div>
<div>
<MyList items={this.state.listItems} />
</div>
<div>
<button onClick={this.onClick.bind(this)}>Reverse</button>
</div>
</div>
)
}
}
ReactDOM.render(<App /> , document.getElementById('example'))
<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>
<section id="example" />