React JS assign separate onclick event to every row of table - reactjs

I have a table which have a setting icon as the last column and whenever a user clicks on it, it should pop open a setting menu. To toggle between active class I used state and passed it to the array.map function, but what is happening is whenever a user clicks on one setting icon all the menus open simultaneously and the reason is they all have same click event and same state variable. How can I change it to where only the clicked setting icon should have its menu opened? My code is given below.
import React, { Component, PropTypes } from 'react';
import '../../../assets/custom_css/tables/unstackable_very_basic_striped_users_table.css';
import { v4 } from 'node-uuid';
import Language from '../../../assets/language';
class UnstackableVeryBasicStripedUsersTable extends Component {
static propTypes = {
rows: PropTypes.array.isRequired
};
constructor(props) {
super(props);
this.getTableRows = this.getTableRows.bind(this);
this.open_setting_menu = this.open_setting_menu.bind(this);
this.state = {
is_action_menu_active: false
};
}
getTableRows() {
const { rows } = this.props;
return rows.map(row => {
let drop_down_class = (this.state.is_action_menu_active) ? "active" : "";
let menu_class = (this.state.is_action_menu_active) ? "transition visible" : "";
return <tr key={v4()}>
{row.map(info => {
return <td key={v4()}>
{info}
</td>
})}
<td>
<div className={"ui right pointing dropdown icon " + drop_down_class} onClick={this.open_setting_menu}>
<i className="setting icon"/>
<div className={"menu " + menu_class}>
<div className="item">Edit</div>
<div className="item">Delete</div>
</div>
</div>
</td>
</tr>
});
}
open_setting_menu() {
this.setState({
is_action_menu_active: !this.state.is_action_menu_active
});
}
render() {
return <table className="ui unstackable celled very basic striped table unstackable_very_basic_striped_table">
<thead>
<tr>
<th>{Language.name}</th>
<th>{Language.role}</th>
<th>{Language.department}</th>
<th>{Language.action}</th>
</tr>
</thead>
<tbody>
{this.getTableRows()}
</tbody>
</table>
}
}
export default UnstackableVeryBasicStripedUsersTable;

If you want to use a single component, you can save the index of the selected row in the state:
import React, { Component, PropTypes } from 'react';
import '../../../assets/custom_css/tables/unstackable_very_basic_striped_users_table.css';
import { v4 } from 'node-uuid';
import Language from '../../../assets/language';
class UnstackableVeryBasicStripedUsersTable extends Component {
static propTypes = {
rows: PropTypes.array.isRequired
};
constructor(props) {
super(props);
this.getTableRows = this.getTableRows.bind(this);
this.open_setting_menu = this.open_setting_menu.bind(this);
this.state = {
selected_row_index: 0,
is_action_menu_active: false
};
}
getTableRows() {
const { rows } = this.props;
return rows.map((row, index) => {
let drop_down_class = (this.state.is_action_menu_active && this.state.selected_row_index === index) ? "active" : "";
let menu_class = (this.state.is_action_menu_active && this.state.selected_row_index === index) ? "transition visible" : "";
return <tr key={v4()}>
{row.map(info => {
return <td key={v4()}>
{info}
</td>
})}
<td>
<div className={"ui right pointing dropdown icon " + drop_down_class} onClick={() => this.open_setting_menu(index)}>
<i className="setting icon"/>
<div className={"menu " + menu_class}>
<div className="item">Edit</div>
<div className="item">Delete</div>
</div>
</div>
</td>
</tr>
});
}
open_setting_menu(index) {
this.setState({
is_action_menu_active: !this.state.is_action_menu_active,
selected_row_index: index
});
}
render() {
return <table className="ui unstackable celled very basic striped table unstackable_very_basic_striped_table">
<thead>
<tr>
<th>{Language.name}</th>
<th>{Language.role}</th>
<th>{Language.department}</th>
<th>{Language.action}</th>
</tr>
</thead>
<tbody>
{this.getTableRows()}
</tbody>
</table>
}
}
export default UnstackableVeryBasicStripedUsersTable;

Related

How to delete table row on button click in React

I wrote an application in React to get value from database, and display its elements in table. It works fine. But my problem is that when I click on delete button in addDelete function, it deletes the content not in the expected way ( I want to delete row sittuated previous to the button). I tryed most of approaches from stack overflow but it didn't work. Here's my code:
import './App.css';
import {Users} from "./users";
import {Email} from "./emails";
import React, {Component} from "react";
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
export class App extends Component{
constructor(props){
super(props);
this.state = {
users: [],
rows: [{}],
data: [],
products: []
};
this.addChild= this.addChild.bind(this);
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users').then((Response) => Response.json())
.then((findresponse) =>
{
this.setState({
users:findresponse
})
//console.log(this.state.users);
});
}
addChild = (product) => {
if(this.state.products.indexOf(product.name) === -1){
this.setState({ products: [...this.state.products, product]})
}
}
addDelete = (i, j) => {
var items = this.state.users;
const index = items.indexOf(items[i]);
if (index > -1) {
items.splice(index, 1);
}
this.setState({
items: items
});
}
render(){
return (
<>
<div className="App">
<main>
<Users callback = {this.addChild} />
<table className = 'list1 table-sm table-striped'>
<thead><tr><th>Name and surname</th><th>Email address</th><th>Delete</th></tr></thead>
<tbody>
{ this.state.users.map(person=><tr className ='name1' key = {person.id}><td>
{person.name}
</td>
<td>
{person.email}
</td>
<td>
<button key = {person.id} onClick={this.addDelete.bind(null, person.id)} className="btn btn-danger btn-sm">Delete</button>
</td></tr>)}
{
this.state.person === 0
? <div className = 'text-center'>There is no persons</div>
: <Email key = {this.state.products.id} products = {this.state.products} />
}
</tbody>
</table>
</main>
</div>
</>
);
}
}
export default App;

Issue with leave animation using React-Flip-Move

I'm using this module for my twitch API app: https://github.com/joshwcomeau/react-flip-move/
and currently having an issue with the leave animation. The enter animation works perfectly, fine, but unforunately, when I click 'x' on one of the channels, the element (in my case a ) moves up and to the right. How do I make it fade out in its current position?
import React, { Component } from 'react';
import { connect } from 'react-redux';
import FlipMove from 'react-flip-move';
import { selectUser, fetchUser, removeUser } from '../actions/index';
class UsersList extends Component {
constructor(props) {
super(props);
this.state = {
show: 'all',
};
this.fetchInitialUsers(this.props.initialUsers);
}
fetchInitialUsers(users) {
users.map(this.props.fetchUser);
}
renderUser(user) {
const { channelData, streamData } = user;
return (
<tr
key={channelData.display_name}
onClick={() => this.props.selectUser(user)}
className='list-item'>
<td>
<img src={channelData.logo} className='user-logo' />
</td>
<td>
{channelData.display_name}
</td>
<td>
{streamData.stream ?
<span className='online'>Online</span> :
<span className='offline'>Offline</span>}
</td>
<span
className="glyphicon glyphicon-remove"
onClick={() => this.props.removeUser(user)}></span>
</tr>
)
}
showOnline() {
this.setState({
show: 'online'
});
}
showOffline() {
this.setState({
show: 'offline'
});
}
showAll() {
this.setState({
show: 'all'
});
}
render() {
return (
<div className='col-sm-4'>
<div className='text-center'>
<div className='btn-group btn-group-sm' role='group'>
<button
className='btn btn-default'
onClick={this.showAll.bind(this)}>
All
</button>
<button
className='btn btn-default'
onClick={this.showOnline.bind(this)}>
Online
</button>
<button
className='btn btn-default'
onClick={this.showOffline.bind(this)}>
Offline
</button>
</div>
</div>
<div className='container'>
<table className='table table-hover'>
<thead>
<tr>
<th>Logo</th>
<th>Channel</th>
<th>Status</th>
</tr>
</thead>
{/* <tbody> */}
<FlipMove
typeName='tbody' enterAnimation='fade'
leaveAnimation='fade'>
{this.props.users.filter(user => {
const { show } = this.state;
const { streamData } = user;
if (show == 'online') {
return streamData.stream;
}
else if (show == 'offline') {
return !streamData.stream;
}
else {
return user;
}
}).map(this.renderUser.bind(this))}
</FlipMove>
{/* </tbody> */}
</table>
</div>
</div>
)
}
}
function mapStateToProps({ users, initialUsers }) {
return { users, initialUsers };
}
export default connect(mapStateToProps, { selectUser, fetchUser, removeUser })(UsersList);
just add maintainContainerHeight="true" to the flipmove attributes

why is this.state.records.amount undefined?

I am in the process of learning React and having some trouble with my state. I am trying to get a function to log this.state.records.amount to my console when the component is rendered but it shows up as undefined. If someone can figure this out it would be VERY much appreciated.
Records component:
class Records extends React.Component {
constructor(props) {
super(props);
this.state = {
records: []
}
this.handleNewRecord = this.handleNewRecord.bind(this);
this.handleDeleteRecord = this.handleDeleteRecord.bind(this);
this.surplus = this.surplus.bind(this);
this.debt = this.debt.bind(this);
this.total = this.total.bind(this);
}
componentDidMount() {
this.setState({
records: this.props.records
})
}
handleNewRecord(record) {
let records = this.state.records.slice();
records.push(record)
this.setState({
records: records
})
}
handleDeleteRecord(record) {
let records = this.state.records.slice();
let index = records.indexOf(record)
records.splice(index, 1)
this.setState({
records: records
})
}
surplus() {
console.log(this.state.records[0].amount)
}
debt() {
console.log("debt")
}
total() {
console.log("total")
}
render() {
const records = this.state.records.map((record) =>
<Record record={record} key={record.id} handleDeleteRecord={this.handleDeleteRecord}/>
)
return (
<div className="container">
<h1>Records</h1>
<div className="row">
<AmountBox panelClass="panel panel-primary" panelHeader="Surplus" calculatedAmount={this.surplus()} />
<AmountBox panelClass="panel panel-warning" panelHeader="Debt" calculatedAmount={this.debt()} />
<AmountBox panelClass="panel panel-success" panelHeader="Total" calculatedAmount={this.total()} />
</div>
<RecordForm handleNewRecord={this.handleNewRecord}/>
<table className="table">
<thead>
<tr>
<th>Date</th>
<th>Title</th>
<th>Amount</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{records}
</tbody>
</table>
</div>
)
}
}
Amount Box component:
class AmountBox extends React.Component {
constructor(props) {
super(props);
}
render () {
return (
<div className="col-md-4">
<div className={this.props.panelClass}>
<div className="panel-heading">
<h3 className="panel-title">{this.props.panelHeader}</h3>
</div>
<div className="panel-body">
<p>
{this.props.calculatedAmount}
</p>
</div>
</div>
</div>
)
}
}
this.state.records[0].amount is undefined because on first render you are setting records to [] (in the constructor).
setState will trigger a second render, but in the first render the changes to state by setState will not apply.
So, you need some defensive code that makes sure that this.state.records have items.
surplus() {
this.state.records.length ? this.state.records[0].amount : 0;
}

React - why is this component not rendering anything?

I am trying to render some child components in a parent component but nothing is rendering. I'm not getting any console errors but there is no render. I can't figure out why this may be happening. The application I am working on is built with React, using a flux architecture.
Here is my code:
Parent Component:
import React from 'react';
import TableWithDataHeader from './TableWithDataHeader.jsx';
import TableWithDataBody from './TableWithDataBody.jsx';
import TableWithDataRowForm from './TableWithDataRowForm.jsx';
import AppStore from '../../stores/AppStore';
export default class TableWithData extends React.Component {
state = {rows: [], isEditing: false};
componentDidMount() {
let json = AppStore.getCells();
let rows = this.state.rows;
for (let key in json) {
{rows[key] = json[key]};
}
this.setState({rows});
console.log(rows);
}
handleEdit = (row) => {
this.setState({isEditing: true});
};
editStop = (formKey) => {
this.setState({isEditing: false});
};
handleSubmit = () => {
console.log('hello');
};
render() {
let {rows, isEditing} = this.state;
console.log(rows);
return (
<div>
<div className="row">
<table className="table table-striped">
<thead>
<TableWithDataHeader />
</thead>
<tbody>
{rows.map(row => this.state.isEditing ?
<TableWithDataRowForm formKey={row.id} key={row.id} editStop={this.editStop(formKey)} handleSubmit={this.handleSubmit} /> :
<TableWithDataBody key={row.id} value={row.historycells.contents} handleEdit={this.handleEdit(row)} />
)}
</tbody>
</table>
</div>
</div>
);
}
}
RowForm:
import React from 'react';
export default class TableWithDataRowForm extends React.Component {
editStop = () => {
this.props.editStop();
};
handleSubmit = (e) => {
e.preventDefault();
this.props.handleSubmit();
};
render() {
return (
<tr>
<td></td>
<td>
<button className=""><i className="btn btn-default" onClick={this.editStop}></i>Cancel</button>
<button className="btn btn-success"><i className="fa fa-cloud" onClick={this.handleSubmit}></i>Save</button>
</td>
</tr>
);
}
}
Table Head:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class TableWithDataHeader extends React.Component {
addHeaders() {
let headerArray = AppStore.getTable().columns;
let headerList = headerArray.map((element, index) => {
return (
<th key={index} id={element.id} className="text-center">{element.name}</th>
);
});
return headerList;
}
render() {
return (
<tr>
{this.addHeaders()}
<th></th>
</tr>
);
}
}
Table Body:
import React from 'react';
export default class TableWithDataBody extends React.Component {
handleEdit() {
this.props.handleEdit();
}
render() {
return (
<tr>
{this.props.histroycells.map(cell => {
return <Cell key={cell.id} value={cell.contents} />
})}
<td>
<button className="btn btn-primary" onClick={this.handleEdit}><i className="fa fa-pencil"></i>Edit</button>
</td>
</tr>
);
}
}
The table header renders fine but neither the body of the table or the edit form shows up at all!
Any help would be much appreciated, especially examples!
Thanks for you time!
Maybe this will help:
Inside your <TableWithDataBody>component, you try to access this.props.historycells, but this isn't passed as a prop.
You render your table rows with:
<TableWithDataBody
key={row.id}
value={row.historycells.contents}
handleEdit={this.handleEdit(row)} />
Maybe if you change render to:
<TableWithDataBody
key={row.id}
historycells={row.historycells} // changed this parameter
handleEdit={this.handleEdit(row)} />
UPDATE:
The line which loops over the props should still read:
{this.props.historycells.map(cell => {
PS: please also fix typo in histroycells.
You have a typo in your code. histroycells in TableWithDataBody should be historycells.

React - appending an empty row to a table

I am currently building an application that renders a table but in the first instance the only thing that renders are the column headers. What I would like to know is how I would then go about giving a user the choice to add a row with no data that they can then edit to add data that they want to put in the cells of the row. The headers are generated by a form with a dynamic amount of inputs so theoretically there could be a lot and the added row would need to reflect this also. I would like to try and avoid using a plugin if possible as well.
The app is being built with React and for the moment I am using flux architecture.
Fast answers with examples would be very much appreciated!
Thanks for your time
Here is where the whole table renders:
import React from 'react';
import TableHeader from './TableHeader.jsx';
import TableBody from './TableBody.jsx';
export default class Table extends React.Component {
render() {
return (
<table className="table table-striped dali-table">
<thead><TableHeader /></thead>
<tbody><TableBody /></tbody>
</table>
);
}
}
Here is the code for rendering the column headers:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class TableHeader extends React.Component {
addHeaders() {
let headerArray = AppStore.getTable().columns;
let headerList = headerArray.map((element, index) => {
return (
<th key={index} id={element.id} className="text-center">{element.name}</th>
);
});
return headerList;
}
render() {
return (
<tr>{this.addHeaders()}</tr>
);
}
}
And I don't really have any code for the body of the table, as that is the bit which I am struggling with!
Okay so I think I'm making some headway! Thanks for the help so far! Here is what I have at the moment (I will be separating it out later, just want to get it working first!):
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class Table extends React.Component {
state = {rows: [], isEditing: false};
render() {
let {rows, isEditing} = this.state;
let headerArray = AppStore.getTable().columns;
const handleEdit = (row) => {
this.setState({isEditing: true});
};
const handleAddRowClickEvent = () => {
let rows = this.state.rows;
rows.push({isNew: true, isEditing: false});
this.setState({rows: rows});
};
return (
<div>
<div className="row" id="table-row">
<table className="table table-striped">
<thead>
<tr>{headerArray.map((element, index) => {
return (
<th key={index} id={element.id} className="text-center">{element.name}</th>
);})}
</tr>
</thead>
<tbody>
{rows.map((row, index) => row.isEditing ?
<RowForm formKey={index} key={index} /> :
<tr key={index}>
{headerArray.map((element, index) => {
return (
<td key={index} id={element.id}></td>
);
})}
<td>
<button onClick={handleEdit(row)}>Edit</button>
</td>
</tr>)}
</tbody>
</table>
</div>
<div className="row">
<div className="col-xs-12 de-button">
<button type="button" className="btn btn-success" onClick={handleAddRowClickEvent}>Add Row</button>
</div>
</div>
</div>
);
}
}
Need help on adding the row don't know what to push into the empty rows array! Thanks
The main idea is add flag to each row so you can know the status for each row, is it in edit mode or display mode
render(){
const handleEdit = (row) => {
// toggle isEditing flag
};
return (<tbody>
{
rows.map((row) => row.isEditing ?
<RowForm formKey={row.id} key={row.id}/> :
<tr key={row.id}>
<td>{row.id}</td>
<td>{row.name}</td>
<button onClick={handleEdit(row)}>
Edit
</button>
</td>
</tr>
}
</tbody>);
}
Check this example source code / demo
Posting some code snippets will help one provide better answers
So, to create a dynamic row you could do something like:
import React from 'react';
import AppStore from '../../stores/AppStore';
export default class TableBody extends React.Component {
addRows() {
// This gets the number of columns in your header from your backend
let noOfColumns = AppStore.getColumnSize();
// loop from 1 to noOfColumns and return:
return (
<td><input placeholder='Enter Value'/></td>
);
}
render() {
return (
<tr>{this.addRows()}</tr>
);
}
}
Then add TableBody to your Table Class.
PS - The above is just code snippets and is somewhat pseudo-code. You also need to handle the 'input field' correctly as per React Documentation recommendation: https://facebook.github.io/react/docs/forms.html

Resources