React - why is this component not rendering anything? - reactjs

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.

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;

How to pass method to functional component in ReactJS

I'm trying to pass method ref to functional component but somehow it doesn't work
Here is the function:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={props.click}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Here is the request I'm trying to pass:
onRemove = (id) => {
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
}
And here is how I pass:
<ArticlesData products={this.state.products} click={this.onRemove}/>
Update:
controller:
router.post('/:id/delete', (req, res) => {
try {
console.log(req.params.id)
productService.deleteOne(req.params.id)
res.status(200)
} catch (error) {
console.log(error)
}
})
service:
function deleteOne(id) {
return Product.deleteOne({_id: id});
}
You need to call the function with parameter id.
I'm assuming your product object has id attribute:
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
Change ArticlesData component's code
from onClick={props.click}
to onClick={() => props.click(product.id)}
Full code:
import React from "react";
import ArticlesData from "./ArticlesData";
export default class SomeName extends React.Component {
onRemove = (id) => {
console.log(id);
fetch(`http://localhost:5000/products/:${id}/delete`, {
method: 'POST'
})
};
render() {
return (
<>
<ArticlesData click={this.onRemove} />
</>
);
}
}
import { FaTrashAlt } from 'react-icons/fa';
const ArticlesData = props => {
return(
props.products.map(product => {
return (
<tr>
<td>{product.name}</td>
<td>{product.description}</td>
<td>{product.price}$</td>
<td>
<span className="removeProduct--Container" onClick={() => props.click(product.id)}>
<FaTrashAlt className="remove--Icon" />
</span>
</td>
</tr>
)
}).reverse()
)
}
export default ArticlesData;
CodeSandbox Demo

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

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;

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