Error in Component Output by Button Click - reactjs

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' />

Related

React .map and setState weird behavior

The following is a simplified version of the part of the entire code.
The entire app is basically supposed to be a note taking app built on React, and currently I'm stuck on its respective note's editing function.
So the following script part basically is supported to do:
Render an array of <Card /> components based on the App this.state.notes array
By clicking the Edit note button it sets the App this.state.noteEditingId state
(so the React instance can know later which generated Card is currnetly being edited by the id)
By clicking the Save Edit button it tries to update the App this.state.notes array with the submitted edit text.
(see, I used a lot of filters to try to achieve this, since I don't have a good idea to how to more nicely achieve this. I believe there should be a nicer way)
But the result is not what I expect.
(While it supposed to achieve updating the expected Card component's notes array with the new note instance's new note "note" text,
it updates the notes array with the different notes's note instance's "note" text. I cannot explain this clearly, since this is an idk-what-is-wrong type of issue to me. )
const Card = (props) => {
const [noteEditing, setNoteEditing] = useState(false);
return (
<div {...props}>
<div>
<div>
<span>
<button onClick={() => {
noteEditing ? setNoteEditing(false) : setNoteEditing(true);
props.thisApp.setState({ noteEditingId: props.config.id })
}}>Edit note</button>
</span>
{noteEditing
?
<div>
<textarea className='__text' />
<button onClick={() => {
let note = document.querySelector('.__text').value
let current_note = props.thisApp.state.notes.filter(a => a.id == props.config.id)[0]
let notesAfterRemoved = props.thisApp.state.notes.filter(a => a.id !== props.config.id)
if (props.thisApp.state.noteEditingId == props.config.id)
{
props.thisApp.setState({
notes: [...notesAfterRemoved, { ...current_note, note: note }]
})
}
}}>
Save Edit
</button>
</div>
: ""
}
</div>
</div>
</div>
)
}
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
]
};
}
render() {
return (
<div>
<h2>
Notes ({this.state.notes.length})
</h2>
<div className='__main_cards'>
<div>
{this.state.notes.map((a, i) => {
return <Card key={i} className="__card" thisApp={this} config={
{
note: a.note,
}
} />
})}
</div>
</div>
</div>
)
}
}
So what can I do fix to make the part work properly? Thanks.
You should pass the current note also and get rid of the filter in card component:
this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})
And then remove this:
let current_note = props.thisApp.state.notes.filter(a => a.id == props.config.id)[0]
And then rather than finding the notes without the edited note you can create a new array with edited data like this:
const x = props.thisApp.state.notes.map((n) => {
if (n.id === props.currentNote.id) {
return {...n, note}
}
return n
})
And get rid off this line:
let notesAfterRemoved = props.thisApp.state.notes.filter(a => a.id !== props.config.id)
Also, change this
noteEditing ? setNoteEditing(false) : setNoteEditing(true);
To:
setNoteEditing(prev => !prev)
As it is much cleaner way to toggle the value.
And I believe, there is no need to check that if the noteEditingId is equal to current active note id.
So you can get rid off that also (Correct me if I am wrong!)
Here's the full code:
const Card = (props) => {
const [noteEditing, setNoteEditing] = useState(false);
const textareaRef = useRef();
return (
<div {...props}>
<div>
<div>
<span>
<button
onClick={() => {
setNoteEditing((prev) => !prev); // Cleaner way to toggle
}}
>
Edit note
</button>
</span>
{noteEditing && (
<div>
<textarea className="__text" ref={textareaRef} />
<button
onClick={() => {
let note = textareaRef.current.value;
const x = props.thisApp.state.notes.map(
(n) => {
if (n.id === props.currentNote.id) {
return { ...n, note };
}
return n;
}
);
props.thisApp.setState({
notes: x,
});
}}
>
Save Edit
</button>
</div>
)}
</div>
</div>
</div>
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
],
};
}
render() {
return (
<div>
<h2>Notes ({this.state.notes.length})</h2>
<div className="__main_cards">
<div>
{this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})}
</div>
</div>
</div>
);
}
}
}
);
props.thisApp.setState({
notes: x,
});
}
}}
>
Save Edit
</button>
</div>
)}
</div>
</div>
</div>
);
};
class App extends React.Component {
constructor() {
super();
this.state = {
notes: [
{
note: "note1.",
id: nanoid(),
},
{
note: "note2.",
id: nanoid(),
},
{
note: "note3.",
id: nanoid(),
},
],
};
}
render() {
return (
<div>
<h2>Notes ({this.state.notes.length})</h2>
<div className="__main_cards">
<div>
{this.state.notes.map((note, i) => {
return (
<Card
key={i}
className="__card"
thisApp={this}
currentNote={note}
/>
);
})}
</div>
</div>
</div>
);
}
}
I hope this is helpful for you!

react state is one state behind button clicks

I am writing a simple react page that renders 2 different html tables based off of which button is clicked on the screen. The issue I am having is that the table that is rendered for each button click is associated with the previous button click. (E.G. if I click button 1 one time then click button 2 the table associated with button 1 will be displayed.)
I am new to react so in order to get the tables to update I refactored my code to hold as much of the state as possible in the App.js class, I created the toggleState callback to associate the button clicks with state change of the parent, and I then pass that to DataProvider via the endpoint property. I realize this is probably where the state / UI disconnect is occurring, but I'm uncertain of the cause since I'm adhering to react principles to the best of my capability.
my class structure is as follows:
App
/ \
/ \
/ \
DataProvider ButtonToggle
|
Table
If it is relevant the table class is building the table based off of an API call, I will add the code for this, but it is not causing me problems so I do not believe it to be the source of the issue.
App.js
import React, { Component } from "react";
import PropTypes from "prop-types";
import DataProvider from "./DataProvider";
import Table from "./Table";
import ButtonToggle from "./ButtonToggle";
class App extends Component {
constructor(props){
super(props);
this.state = {
input : 'employees',
endpoint : "api/employees/"
};
console.log("constructor app: " + this.state.input + "\n" + this.state.endpoint);
}
toggleState(input) {
if(input == "employees") {
this.setState({input : input, endpoint: "api/employees/"});
}
else {
this.setState({input : input, endpoint: "api/categories/"});
}
console.log("toggleState " + this.state.input + "\n" + this.state.endpoint);
}
render() {
return (
<div className="col-lg-12 grid-margin">
<div className="card">
<div className="card-title">
<div className="row align-items-center justify-content-center">
<div className="col-3"></div>
<div className="col-6">
<h1> Striped Table</h1>
</div>
<div className="col-3"></div>
</div>
<ButtonToggle toggleInput={ (input) => this.toggleState(input)}/>
</div>
<div className="card">
<div className="card-title"></div>
<div className="card-body">
<DataProvider endpoint={this.state.endpoint}
render={data => <Table data={data} />} />
</div>
</div>
</div>
</div>
);
}
}
export default App;
DataProvider.js
class DataProvider extends Component {
static propTypes = {
endpoint: PropTypes.string.isRequired,
render: PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.state = {
data: [],
loaded: false,
placeholder: "Loading..."
};
}
componentWillReceiveProps(props) {
console.log("dataprov: " + this.props.endpoint);
this.componentDidMount();
}
componentDidMount() {
fetch(this.props.endpoint)
.then(response => {
if (response.status !== 200) {
return this.setState({ placeholder: "Something went wrong" });
}
return response.json();
})
.then(data => this.setState({ data: data, loaded: true }));
}
render() {
const { data, loaded, placeholder } = this.state;
return loaded ? this.props.render(data) : <p>{placeholder}</p>;
}
}
export default DataProvider;
ButtonToggle.js
class ButtonToggle extends Component {
constructor (props) {
super(props);
}
render() {
return (
<div className="row align-items-center justify-content-center">
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'categories')}> Categories </button>
</div>
<div className="col-3 center-in-div">
<button type="button" className="btn btn-info btn-fw" onClick={this.props.toggleInput.bind(this, 'employees')}>
Employees
</button>
</div>
<div className="col-6"></div>
</div>
);
}
}
export default ButtonToggle;
Table.js : I don't think this is a problem, but I may stand corrected.
import React from "react";
import PropTypes from "prop-types";
import key from "weak-key";
const Table = ({ data }) =>
!data.length ? (
<p>Nothing to show. Records: {data.length} </p>
) : (
<div className="table-responsive">
<h2 className="subtitle">
Showing <strong>{data.length} items</strong>
</h2>
<table className="table table-hover">
<thead>
<tr>
{Object.entries(data[0]).map(el => <th key={key(el)}>{el[0]}</th>)}
</tr>
</thead>
<tbody>
{data.map(el => (
<tr key={el.id}>
{Object.entries(el).map(el => <td key={key(el)}>{el[1]}</td>)}
</tr>
))}
</tbody>
</table>
</div>
);
Table.propTypes = {
data: PropTypes.array.isRequired
};
export default Table;
Below is the minimum working code I could come up with. Your Button and Table components can be dumb components which will get data from parent component and will present it.
Your Parent or container component will have logic to set the properties for Button and Table component.
As Table and Button components are dumb you can go with functional components.
I have added the code for calling api (I have tried to mimic the api call) and getting data in same parent component, you can separate it out.
You can work on style and validations as per your needs.
Let me know if you need any further help.
class ParentComponent extends Component {
constructor() {
super();
this.state = {
name: "Category"
}
this.onBtnClick = this.onBtnClick.bind(this);
}
componentDidMount() {
this.getData(this.state.name)
}
getData(name) {
if (name === "Category") {
this.apiCall("/Category").then((data) => {
this.setState({ data: data })
})
} else {
this.apiCall("/Employee").then((data) => {
this.setState({ data: data })
})
}
}
apiCall(url) {
return new Promise((res, rej) => {
setTimeout(() => {
if (url === "/Employee") {
res([{ "Emp Name": "AAA", "Emp Age": "20" }, { "Emp Name": "BBB", "Emp Age": "40" }])
} else {
res([{ "Cat Id": "XXX", "Cat Name": "YYY" }, { "Cat Id": "MMM", "Cat Name": "NNN" }])
}
}, 1000)
});
}
onBtnClick(name) {
let newName = "Category"
if (name === newName) {
newName = "Employee"
}
this.setState({ name: newName, data: [] }, () => {
this.getData(newName);
})
}
render() {
return (<>
<ButtonComponent name={this.state.name} onBtnClick={this.onBtnClick}></ButtonComponent>
<TableComponent data={this.state.data} />
</>)
}
}
const ButtonComponent = ({ name, onBtnClick }) => {
return <Button onClick={() => { onBtnClick(name) }}>{name}</Button>
}
const TableComponent = ({ data }) => {
function getTable(data) {
return < table >
<thead>
<tr>
{getHeading(data)}
</tr>
</thead>
<tbody>
{getRows(data)}
</tbody>
</table >
}
function getHeading(data) {
return Object.entries(data[0]).map((key) => {
return <th key={key}>{key[0]}</th>
});
}
function getRows(data) {
return data.map((row, index) => {
return <tr key={"tr" + index}>
{Object.entries(data[0]).map((key, index) => {
console.log(row[key[0]]);
return <td key={"td" + index}>{row[key[0]]}</td>
})}
</tr>
})
}
return (
data && data.length > 0 ?
getTable(data)
: <div>Loading....</div>
)
}

Update list of displayed components on deletion in React

in the beginning on my path with React I'm creating simple to-do app where user can add/remove task which are basically separate components.
I create tasks using:
addTask(taskObj){
let tasksList = this.state.tasksList;
tasksList.push(taskObj);
this.setState({tasksList : tasksList});
}
I render list of components (tasks) using following method:
showTasks(){
return (
this.state.tasksList.map((item, index) => {
return <SingleTask
taskObj={item}
removeTask = {(id) => this.removeTask(id)}
key = {index}/>;
})
);
}
method to remove specific task takes unique ID of task as an argument and based on this ID I remove it from the tasks list:
removeTask(uID){
this.setState(prevState => ({
tasksList: prevState.tasksList.filter(el => el.id != uID )
}));
}
But the problem is, when I delete any item but the last one, it seems like the actual list of components is the same only different objects are passed to those components.
For example:
Lets imagine I have 2 created componentes, if I set state.Name = 'Foo' on the first one, and state.Name='Bar' on the second one. If I click on remove button on the first one, the object associated to this component is removed, the second one becomes first but it's state.Name is now 'Foo' instead of 'Bar'.
I think I'm missing something there with correct creation/removing/displaying components in react.
Edit:
Method used to remove clicked component:
removeCurrentTask(){
this.props.removeTask(this.props.taskObj.id);
}
SingleTask component:
class SingleTask extends Component{
constructor(props) {
super(props);
this.state={
showMenu : false,
afterInit : false,
id: Math.random()*100
}
this.toggleMenu = this.toggleMenu.bind(this);
}
toggleMenu(){
this.setState({showMenu : !this.state.showMenu, afterInit : true});
}
render(){
return(
<MDBRow>
<MDBCard className="singleTaskContainer">
<MDBCardTitle>
<div class="priorityBadge">
</div>
</MDBCardTitle>
<MDBCardBody className="singleTaskBody">
<div className="singleTaskMenuContainer">
<a href="#" onClick={this.toggleMenu}>
<i className="align-middle material-icons">menu</i>
</a>
<div className={classNames('singleTaskMenuButtonsContainer animated',
{'show fadeInRight' : this.state.showMenu},
{'hideElement' : !this.state.showMenu},
{'fadeOutLeft' : !this.state.showMenu && this.state.afterInit})}>
<a
title="Remove task"
onClick={this.props.removeTask.bind(null, this.props.taskObj.id)}
className={
classNames(
'float-right btn-floating btn-smallx waves-effect waves-light listMenuBtn lightRed'
)
}
>
<i className="align-middle material-icons">remove</i>
</a>
<a title="Edit title"
className={classNames('show float-right btn-floating btn-smallx waves-effect waves-light listMenuBtn lightBlue')}
>
<i className="align-middle material-icons">edit</i>
</a>
</div>
</div>
{this.props.taskObj.description}
<br/>
{this.state.id}
</MDBCardBody>
</MDBCard>
</MDBRow>
);
}
}
Below visual representation of error, image on the left is pre-deletion and on the right is post-deletion. While card with "22" was deleted the component itself wasn't deleted, only another object was passed to it.
Just to clarify, the solution was simpler than expected.
In
const showTasks = () => taskList.map((item, index) => (
<SingleTask
taskObj={item}
removeTask ={removeTask}
key = {item.id}
/>
)
)
I was passing map index as a key, when I changed it to {item.id} everything works as expected.
In short, in the statement tasksList.push(<SingleTask taskObj={taskObj} removeTask ={this.removeTask}/>);, removeTask = {this.removeTask} should become removeTask = {() => this.removeTask(taskObj.id)}.
However, I would reconsider the way the methods addTask and showTasks are written. While the way you have written isn't wrong, it is semantically unsound. Here's what I would do:
addTask(taskObj){
let tasksList = this.state.tasksList;
tasksList.push(taskObj);
this.setState({tasksList : tasksList});
}
showTasks(){
return (
this.state.tasksList.map((item, index) => {
return <SingleTask
taskObj={item}
removeTask ={() => this.removeTask(item.id)}/>;
})
);
}
const SingleTask = (task) => {
const { taskObj } = task;
return <div onClick={task.removeTask}>
{ taskObj.title }
</div>
}
// Example class component
class App extends React.Component {
state = {
tasksList: [
{ id: 1, title: "One" },
{ id: 2, title: "Two" },
{ id: 3, title: "Three" },
{ id: 4, title: "Four" }
]
}
addTask = (taskObj) => {
let tasksList = this.state.tasksList;
tasksList.push(taskObj);
this.setState({tasksList : tasksList});
}
showTasks = () => {
return (
this.state.tasksList.map((item, index) => {
return <SingleTask
key={index}
taskObj={item}
removeTask ={() => this.removeTask(item.id)}/>;
})
);
}
removeTask(id) {
this.setState(prevState => ({
tasksList: prevState.tasksList.filter(el => el.id != id )
}));
}
render() {
return (
<div className="App">
<div> {this.showTasks()} </div>
</div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.body
);
<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>

Iterate Item Inside JSX React Native [duplicate]

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>
);
}
}

React JS - Event Handler in a dynamic list

I'm bringing a API s' content based on a dynamic list and I'm trying to apply a mouserEnter on each li. The event results by toggling content in the each list item. The event is working but it is toggling content in all the list items all at once, but I want it to toggle only the content that matches with the list item that is receiving the mouseEnter.
import _ from 'lodash';
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
export default class Dribbble extends React.Component {
constructor(props) {
super(props);
this.state = {
work: [],
hover: false
};
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
}
handleMouseEnter(){
this.setState({ hover: true })
}
handleMouseLeave(){
this.setState({ hover: false })
}
componentDidMount() {
this.ShotList();
}
ShotList() {
return $.getJSON('https://api.dribbble.com/v1/shots?per_page=3&access_token=41ff524ebca5e8d0bf5d6f9f2c611c1b0d224a1975ce37579326872c1e7900b4&callback=?')
.then((resp) => {
this.setState({ work: resp.data.reverse() });
});
}
render() {
const works = this.state.work.map((val, i) => {
return <li key={i} className="box"
onMouseEnter={this.handleMouseEnter}
onMouseLeave={this.handleMouseLeave}
>
{!this.state.hover ?
<div>
<img className="cover" src={val.images.normal} />
<div className="bar">
<h2>{val.title}</h2>
<span>{val.views_count}</span>
<i className="fa fa-eye fa-2x" aria-hidden="true"></i>
</div>
</div>
: null}
{this.state.hover ?
<div>
<h3>{val.user.name}</h3>
<img className="avatar img-circle" src={val.user.avatar_url}/>
<p>{val.description}</p>
</div>
:
null
}
</li>
});
return <ul>{works}</ul>
}
}
Here is my code:
There are couple of issues in your example, firstly as #aherriot states you should move the ul outside the map.
Next i would set this.state.hover to be the id of the item being hovered over on onMouseEnter.
The below snippet shows a basic example of this working that should be easy enough to adapt to your code.
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
items: [{id: 1, name: 'Fred'}, {id: 2, name: 'Albert'}, {id: 3, name: 'Jane'}],
hover: false,
}
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
this.renderItem = this.renderItem.bind(this);
}
handleMouseEnter(id){
console.log(`handleMouseEnter this.setState({ hover: ${id} })`);
this.setState({ hover: id })
}
handleMouseLeave(){
console.log('handleMouseLeave this.setState({ hover: false })');
this.setState({ hover: false })
}
renderItem(item, index) {
let content = [];
content.push(
<span>ID: {item.id}, Name: {item.name}</span>
);
if(this.state.hover === item.id) {
console.log('display " - hovering" for item id: ' + item.id);
content.push(
<span> - hovering</span>
);
}
return (
<li key={item.id}
onMouseEnter={() => this.handleMouseEnter(item.id)}
onMouseLeave={this.handleMouseLeave}
>
{content}
</li>
)
}
render() {
return <ul>
{this.state.items.map(this.renderItem)}
</ul>
}
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="root"></div>
Maybe you should move the <ul> tag outside of this.state.work.map You only want one <ul> to show up, not one for each element.
You can place it at the bottom inside your div tag instead: return (<div><ul>{works}</ul></div>)

Resources