ReactJS. How to get states of elements inside react component - reactjs

Help me please to get states of elements inside react component or other some custom features with information which I need. Every of td tags contains react component Block1.
Simplified code structure below
class Block2 extends React.Component {
render() {
return (
<table>
<tbody>
<tr>
<td>
<Block1 />
</td>
<td>
<Block1 />
</td>
</tr>
</tbody>
</table>
)}}
Block1 - react component which contain div elements.
Block2 is inside component Block3. How to get states of Block1 from Block3 by click on some button?
Now, I can get list of Block1 and may see props, but I can't see states. Or I can get DOM td elements and see children classNames (which I looking for in states) but I can't see props...

Unless you use libraries like Redux, you have to do the following to solve your problem:
Store the state inside Block3 and not Block1. Then pass any function that changes this state as props from Block3 to Block2 to Block1. When any change occurs in Block1, call this function. The pattern should be:
class Block3 {
changeState(value) {
this.setState({ stateValue: value });
}
render() {
return (
<Block2 changeState={this.changeState}/>
)
}
}
class Block2 extends React.Component {
render() {
return (
<table>
<tbody>
<tr>
<td>
<Block1 changeState={this.props.changeState} />
</td>
<td>
<Block1 changeState={this.props.changeState} />
</td>
</tr>
</tbody>
</table>
)}}
class Block1 {
changeHandler(ev) {
this.props.changeState(ev.target.value);
}
render() {
return (
<button onClick={this.changeHandler}/>
)
}
}
If you really want to access the child state inside a parent component, consider using refs:
class Block2 {
render() {
return <Block1 ref={ (childComponent) => { this.block1Child = childComponent; } } />;
// Now you may use this.block1Child to access child component's state eg: this.block1Child.setState({})
}
}
EDIT: My solution after seeing your code:
import React, { Component } from 'react';
import './Hall.css';
class HallCol extends Component {
constructor(props) {
super(props);
this.state = {
chair_cls:'chair-default'
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(e) {
let chair_cls = null;
if(this.state.chair_cls==="chair-default") {
chair_cls = 'chair-sold';
this.props.updateCount(1, this.props.seat_number);
} else {
chair_cls = 'chair-default';
this.props.updateCount(-1, this.props.seat_number);
}
this.setState({
chair_cls: chair_cls
});
}
render(){
return(
<div className={this.state.chair_cls} onClick={this.handleClick}>
<div className="backrest">
<p>{this.props.seat_number}</p>
</div>
<div className="armrest-left">
</div>
<div className="armrest-right">
</div>
</div>
);
}
}
class Table extends React.Component {
constructor() {
super();
this.genRow = this.genRow.bind(this); // this is method binding
}
genRow() {
var rows = this.props.rows;
return rows.map(function(v, i) {
var tmp = v.map(function(v2, j) {
return (
<td key={'td' + i + '_' + j} className='chair-cell' >
{v2}
</td>
);
});
return (
<tr key={'tr' + i}>
{tmp}
</tr>
)
});
}
render() {
return (
<table className="hall-grid" >
<tbody>
{this.genRow()}
</tbody>
</table>
);
}
}
class Hall extends React.Component {
constructor(props) {
super(props);
var rows_num = this.props.rows;
var cols_num = this.props.cols;
this.AddRow = this.AddRow.bind(this);
this.updateSeats = this.updateSeats.bind(this);
var row = [];
for (var i = 0; i < rows_num; i++) {
var col = [];
for (var k = 0; k< cols_num; k++) {
col.push(<HallCol row = {i+1} seat_number = {k+1} updateCount={this.updateSeats} />); // bind the passed function to parent
}
row.push(col);
}
this.state = {
rows: row,
bookedSeats: [],
count: 0
};
}
AddRow() {
let newRows = this.state.rows;
newRows.push([0, 0]);
this.setState({rows: newRows});
}
updateSeats(val, seat_number) {
let bookedSeatsUpdated;
if( val === 1 ) {
bookedSeatsUpdated = this.state.bookedSeats.concat(seat_number);
} else {
bookedSeatsUpdated = this.state.bookedSeats;
let index = bookedSeatsUpdated.indexOf(seat_number);
bookedSeatsUpdated.splice(index,1);
}
this.setState({
bookedSeats: bookedSeatsUpdated,
count: this.state.count + val
});
}
render() {
return (
<div className="hall">
<Table rows={this.state.rows} />
<button className = "btn-default" onClick={() => {
alert(this.state.count + 'seats : ' + this.state.bookedSeats);
} }>
TOTAL SOLD
</button>
</div>
);
}
}
export default Hall;

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;

Raised Button onClick triggers different function

I have two components, RaisedButton and TableList. Tablelist return select rows and the same is updated in the state (currentSelectedRows). RaisedButton simply consoles the currentSelectedRows. Now the problem statement:
onClick of RaisedButton it consoles the state properly (using approveSelected) till the time updateSelectedRows does not update the state. Once the state is updated inside updateSelectedRows method, onClick of RaisedButton component first calls updateSelectedRows then approveSelected. Below is the code.
export default class MyList extends React.Component {
constructor(props) {
super(props);
this.state = {
pendingList:[],
currentSelectedRows:[]
}
}updateSelectedRows(selectedRows){
console.log("updateCalled");
this.setState({
currentSelectedRows:selectedRows
});
};
approveSelected() {
console.log("approve selected");
console.log(this.state.currentSelectedRows);
};
render() {
return (
<div className="container">
<div className="row_align_right">
<RaisedButton label="APPROVE" backgroundColor={MUIColors.lightGreen500} labelColor={MUIColors.white} onClick={this.approveSelected.bind(this)} />
</div>
<div className="content">
<div className="">
<TableList
selectedRows={this.state.currentSelectedRows}
updateSelectedRows={this.updateSelectedRows.bind(this)}
/>
</div>
</div>
</div>
)
}
}
Any advice would be of great help.
Thanks
You didn't provide the code for TableList so it's hard to know what could be the problem but it seems to work when you just pass a row id upwards to the parent:
const usersList = [
{ name: 'John', age: 33 },
{ name: 'Jane', age: 32 },
{ name: 'David', age: 28 },
{ name: 'Eve', age: 29 },
];
class Row extends React.Component {
onClick = e => {
const { onClick, rowId } = this.props;
onClick(rowId)
}
render() {
const { user } = this.props;
return (
<tr onClick={this.onClick}>
<td>{user.name}</td>
<td>{user.age}</td>
</tr>
);
}
}
class TableList extends React.Component {
onClick = rowId => {
this.props.updateSelectedRows(rowId);
}
render() {
return (
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
{
usersList.map((user, i) => <Row key={i} rowId={i} user={user} onClick={this.onClick}/>)
}
</tbody>
</table>
);
}
}
class MyList extends React.Component {
constructor(props) {
super(props);
this.state = {
pendingList: [],
currentSelectedRows: []
}
} updateSelectedRows(selectedRows) {
console.log("updateCalled");
this.setState({
currentSelectedRows: selectedRows
});
};
approveSelected() {
console.log("approve selected");
console.log(this.state.currentSelectedRows);
};
render() {
return (
<div className="container">
<div className="row_align_right">
<button onClick={this.approveSelected.bind(this)}>Click</button>
</div>
<div className="content">
<div className="">
<TableList
selectedRows={this.state.currentSelectedRows}
updateSelectedRows={this.updateSelectedRows.bind(this)}
/>
</div>
</div>
</div>
)
}
}
ReactDOM.render(<MyList />, 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>

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 JS assign separate onclick event to every row of table

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;

reactjs - How to render a component which render return a component?

for the following example I have a component GetCurrentVisitor which renders Visitors.
However it will only render the <h1> tag and the table is empty. I suspect I need to use ReactDOM to render Vistors component as well. But how to do it?
var VISITORS = [
{
first_name: 'Harry',
last_name: 'Potter'
},
{
first_name: 'Hermione',
last_name: 'Granger'
}
]
class GetCurrentVisitors extends React.Component {
constructor(props) {
super(props);
this.state = {
visitors: VISITORS
}
}
render () {
return (
<div>
<h1>Current visitors</h1>
<Visitors visitors={this.state.visitors} />
</div>
);
}
}
class Visitors extends React.Component {
constructor(props) {
super(props);
}
render () {
return (
<table>
{this.props.visitors.forEach(
function(visitor) {
<tr>
<td>
{console.log('from: ', visitor.first_name)}
{visitor.first_name}
</td>
</tr>
})}
</table>
);
}
}
ReactDOM.render(
<GetCurrentVisitors />, document.getElementById('getcurrentvisitors'))
In this case you should use .map instead of .forEach
{this.props.visitors.map(function(visitor, index) {
return <tr key={index}>
<td>{ visitor.first_name } </td>
</tr>
})}
Example
You can also are able to use .forEach but in another way fiddle
render () {
let itemList = [];
this.props.visitors.forEach(function(visitor,index) {
itemList.push(<tr key={index}><td>
{visitor.first_name}
</td></tr>
)
})
return (
<table>
{itemList}
</table>
);
}
As for me Array.prototype.map more easy to use with React. It just another example.
Thanks

Resources