export class UsersTable extends React.Component {
constructor() {
super();
this.state = {
info: null
};
}
componentWillMount() {
fetch("http://localhost:8081/milltime/getUsers")
.then(res => res.json())
.then(info => {
this.setInfo(info);
});
}
setInfo(info) {
const state = this.state;
state['info'] = info;
this.setState(state);
}
render() {
const info = this.state.info;
if (!this.state.info) {
return null;
}
let listItems = [];
for (var i = 0; i < info['mta:getUsersResponse']['mta:users'].length; i++) {
listItems.push(
<tr>
<td>{info['mta:getUsersResponse']['mta:users'][i]['mta:UserId']}</td>
<td>{info['mta:getUsersResponse']['mta:users'][i]['mta:FullName']}</td>
<td>{info['mta:getUsersResponse']['mta:users'][i]['mta:CostHour']}</td>
</tr>);
}
return(
<div className="usersTable">
<Table striped bordered condensed responsive hover>
<thead>
<tr>
<th>Id</th>
<th>Full Name</th>
<th>Hour cost</th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</Table>
</div>
);
}
}
This is the code I have for a table that get users and displays 3 columns of data. What I am having problems doing is being able to select a table and by selecting that table get the data in that cell and use it to search with the help of the id of the user in the selected cell. Has anyone got a neat solution? I'm using React bootstrap.
Bind your onClick handler when your creating the row.
See comments in code.
https://reactjs.org/docs/handling-events.html
export class UsersTable extends React.Component {
constructor() {
super();
this.state = {
info: null
};
}
componentWillMount() {
fetch("http://localhost:8081/milltime/getUsers")
.then(res => res.json())
.then(info => {
this.setInfo(info);
});
}
setInfo(info) {
const state = this.state;
state['info'] = info;
this.setState(state);
}
onSelectedRow(user, clickEvent){
//your user object and the click event
//clickEvent.currentTarget = the cell clicked
}
render() {
const info = this.state.info;
if (!this.state.info) {
return null;
}
let listItems = [];
for (var i = 0; i < info['mta:getUsersResponse']['mta:users'].length; i++) {
const user = info['mta:getUsersResponse']['mta:users'][i]; //dryer
//Bind you onclick handler to the context and you user object (or id if thats what you want)
listItems.push(
<tr onClick={this.onSelectedRow.bind(this, user)}>
<td>{user['mta:UserId']}</td>
<td>{user['mta:FullName']}</td>
<td>{user['mta:CostHour']}</td>
</tr>);
}
return(
<div className="usersTable">
<Table striped bordered condensed responsive hover>
<thead>
<tr>
<th>Id</th>
<th>Full Name</th>
<th>Hour cost</th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</Table>
</div>
);
}
}
Api requests should be handled in componentDidMount lifecycle event as described in React docs.
Also, your are mutating your state on setInfo and this is not a good practice either. You can directly update your state like:
setInfo(info) {
this.setState({
info: info,
})
}
or simply using object shorthand
setInfo(info) {
this.setState({
info,
})
}
Should your api change in the future, you are gonna have problems replacing all the mta:** in your code. Why don't you map them upon state?
this.setState({
info: {
users: info['mta:getUsersResponse']['mta:users'].map(user => ({
id: user['mta:UserId'],
fullName: user['mta:FullName'],
costHour: user['mta:CostHour'],
}))
}
})
Click handling becomes easier from now, just create a UserRow component, send user as prop and propagate changes on onClick.
const UserRow = ({ user, onClick }) =>
<tr onClick={onClick}>
<td>{user.id}</td>
<td>{user.fullName}</td>
<td>{user.costHour}</td>
</tr>
Now you can map though your state and propagate props to it:
const UserRow = ({ user, onClick }) =>
<tr onClick={onClick}>
<td>{user.id}</td>
<td>{user.fullName}</td>
<td>{user.costHour}</td>
</tr>
class App extends React.Component {
constructor() {
super()
this.state = {
info: {
users: [
{ id: 0, fullName: 'User 1', costHour: 100 },
{ id: 1, fullName: 'User 2', costHour: 50 },
{ id: 2, fullName: 'User 3', costHour: 150 }
]
}
}
this.handleUserClick = this.handleUserClick.bind(this)
}
handleUserClick(user) {
console.log(`user ${user.id} has been clicked`)
}
render() {
return (
<div className="usersTable">
<table striped bordered condensed responsive hover>
<thead>
<tr>
<th>Id</th>
<th>Full Name</th>
<th>Hour cost</th>
</tr>
</thead>
<tbody>
{this.state.info.users.map(user =>
<UserRow
key={user.id}
user={user}
onClick={this.handleUserClick.bind(this, user)}
/>
)}
</tbody>
</table>
</div>
)
}
}
ReactDOM.render(
<App />,
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>
Related
I am working with a table and I am trying to figure out is there a way to set the default order to ASC when the page loads?
class Orders extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [
{orders: 'Vanilla', date: '03/15/1990'},
{orders: 'Chocolate', date: '03/15/1989'},
],
sortingOrder: 'ASC'
};
this.sortBy.bind(this);
}
renderTableData() {
return this.state.data.map((data, index) => {
const{orders, date} = data
return (
<tr key={index}>
<td>{orders}</td>
<td>{date}</td>
</tr>
)
})
}
sortBy(sortedKey) {
const data = this.state.data;
let sortingOrder = this.state.sortingOrder;
if(sortingOrder === 'ASC') {
sortingOrder = 'DESC';
data.sort((a,b) => b[sortedKey].localeCompare(a[sortedKey]))
}
else {
sortingOrder = 'ASC';
data.sort((a,b) => a[sortedKey].localeCompare(b[sortedKey]))
}
this.setState({data, sortingOrder })
}
render() {
return (
<table id="orders">
<thead>
<tr className="header">
<th>Order</th>
<th onClick={() => this.sortBy('date')}>Date</th>
</tr>
</thead>
<tbody>
{this.renderTableData()}
</tbody>
</table>
);
}
}
I tried calling this.sortBy() in my render method first, but that gave me an error about too many calls. Any ideas?
Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
you can do on componentDidMount for default
componentDidMount(){
this.sortBy('ASC');
}
you should use the comopnentDidMount method
https://reactjs.org/docs/react-component.html#componentdidmount
componentDidMount(){
this.sortBy('date');
}
Here is my App.js
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
employees: []
};
}
loadEmployees() {
fetch('http://localhost:8080/Employee')
.then((response) => response.json())
.then((responseData) => {
this.setState({
employees: responseData
});
});
}
componentDidMount() {
this.loadEmployees();
}
render() {
return (
<EmployeeList employees={this.state.employees}/>
)
}
}
export default App;
Here is the Employee.js
class Employee extends React.Component {
render() {
return (
<tr>
<td>{this.props.employee.firstName}</td>
<td>{this.props.employee.lastName}</td>
<td>{this.props.employee.description}</td>
</tr>
)
}
}
export default Employee;
And EmployeeList.js
class EmployeeList extends Component {
render(){
var employees = this.props.employees.map(employee =>
<Employee key={employee._links.self.href} employee={employee}/>
);
return (
<table>
<tbody>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</tbody>
</table>
)
}
}
export default EmployeeList;
I have spring-boot rest point at the http://localhost:8080/Employee
Here is my Controller class.
#RestController
public class EmployeeController {
#Autowired
EmployeService employeeService;
#RequestMapping(value="/Employee")
public List<Employee> defaultEmployee() {
return employeeService.getAllData();
}
}
I am returning List<Employee>, it turns to JSON, when I try to catch this result, here is the error.
The Rest returns employees from the DB, but i can not map it into the list.
Can anyone tell me what is the problem?
Make sur that the this.state.employees is an array , the things in react the component will render no mather if the value of the props are empty or not
and the map function will trow an error if you try to pass an empty array
so on your EmployeeList.js added this code
class EmployeeList extends Component {
render(){
var employees = <div>Loading</div>
if(this.props.employees.length > 0){
employees = this.props.employees.map(employee =>
<Employee key={employee._links.self.href} employee={employee}/>
);
}
return (
<table>
<tbody>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Description</th>
</tr>
{employees}
</tbody>
</table>
)
}
}
Please test it and let me know if it work
I am trying to edit a row of table containing person objects. My idea is to show the table and after clicking on the row to edit, the row changes to 2 textBoxes (Nameand surnameto edit) and a button to confirm edit operation. When I run the program the table shows 4 times its size(I have 4 rows) and when I click on a random row disappears 4 rows and the table becomes 3 times its size(12 rows).
What is causing the fail? Thanks for your time
The code:
class EditPersons extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: null
};
this.editPerson = this.editPerson.bind(this);
}
componentDidMount() {
this.props.fetchData('http://localhost:9536/persons/');
}
editPerson(person) {
this.setState(
{ editing: person.PersonId }
);
}
renderPersonOrEdit(person) {
if (this.state.editing === person.PersonId) {
console.log('editing: ' + person.PersonId); //test ok
//Here comes the 2 textBoxes and the edit-button
}
else {
return (
<tbody>
{this.props.persons.map((person, i) => {
return(
<tr key={i}>
<td onClick={()=>this.editPerson.(person)}><Link>{person.Name}<Link></td>
<td>{person.Surname}</td>
</tr>
);
})}
</tbody>
);
}
}
render() {
if (this.props.hasErrored) {
return <p>Downloading has failed!</p>;
}
if (this.props.isLoading) {
return <p>Downloading…</p>;
}
return (
<div>
<table id="myTable">
<thead>
<tr>
<th>Person name</th>
<th>Person Surname</th>
</tr>
</thead>
{this.props.persons.map((person) => {
return this.renderPersonOrEdit(person);
})}
</table>
<Link to="/project" className="btn btn-primary btn-xs" style={{marginTop: 20}}>Back</Link>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
personss: state.persons,
hasErrored: state.personsHasErrored,
isLoading: state.personsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(personsFetchData(url))
};
};
EditPersons.propTypes = {
fetchData: PropTypes.func.isRequired,
persons: PropTypes.array.isRequired,
hasErrored: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
export default connect(mapStateToProps, mapDispatchToProps)(EditPersons);
In your render() function you are already mapping over the elements and then in the renderPersonOrEdit you are doing the same, change your code to map at only the render function
class EditPersons extends React.Component {
constructor(props) {
super(props);
this.state = {
editing: null
};
this.editPerson = this.editPerson.bind(this);
}
componentDidMount() {
this.props.fetchData('http://localhost:9536/persons/');
}
editPerson(person) {
this.setState(
{ editing: person.PersonId }
);
}
renderPersonOrEdit() {
if (this.state.editing !== null) {
console.log('editing: ' + person.PersonId); //test ok
//Here comes the 2 textBoxes and the edit-button
//set editing to null after editing the contents
}
else {
return <tbody>
{this.props.persons.map((person) => {
return (
<tr key={person.PersonId}>
<td onClick={()=>this.editPerson.(person)}><Link>{person.Name}<Link></td>
<td>{person.Surname}</td>
</tr>
);
})}
</tbody>
}
}
render() {
if (this.props.hasErrored) {
return <p>Downloading has failed!</p>;
}
if (this.props.isLoading) {
return <p>Downloading…</p>;
}
return (
<div>
<table id="myTable">
<thead>
<tr>
<th>Person name</th>
<th>Person Surname</th>
</tr>
</thead>
{this.renderPersonOrEdit()}
</table>
<Link to="/project" className="btn btn-primary btn-xs" style={{marginTop: 20}}>Back</Link>
</div>
);
}
}
I'm learning react and I'm trying to figure out how I would put this information into a table. I know there is
<table></table>
in html so I'm assuming I would use something like that within the javascript. I'm trying to create a table where the month of the birthday contains the name and the date of each person.
Any advice/help would be great!
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
March: [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
render() {
return (
<div>
{Object.keys(this.state.birthdays).reduce((birthdays, month) => {
return birthdays.concat(this.state.birthdays[month].map((bday) => {
return (
<p>{bday.name} - {bday.date}</p>
);
}));
}, [])}
</div>
);
}
ReactDOM.render(<App />,
document.getElementById('container'));
Excuse the formatting of the code.
You need to first map the keys and then iterate over the birthdays object. Also put single quotes on March like 'March'
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
'March': [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
renderTableRows = () => {
var rows = [];
Object.keys(this.state.birthdays).forEach((month, key) => { var birthdays = this.state.birthdays[month];
birthdays.forEach((obj) => {
rows.push (
<tr>
<td>{month}</td>
<td >{obj.name}</td>
<td>{obj.date}</td>
</tr>
)
})
})
var tby = null;
tby = <tbody>
{rows.map((obj, key) => {
return (
obj
)
})}
</tbody>
return tby;
}
render() {
return (
<div>
<table className="table table-striped">
<thead>
<tr>
<th>Month</th>
<th>Name</th>
<th>Date</th>
</tr>
</thead>
{this.renderTableRows()}
</table>
</div>
);
}
}
ReactDOM.render(<App />,
document.getElementById('container'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.js"></script>
<div id="container"></div>
You're using jsx which is an extension of javascript by including html tags, this means that you can use them within your javascript code.
You need to open the <table> tag before iterating over your birthdays so you can create the <tr> (rows) inside it.
return <table>
{
birthdays.map((bday) => (
<tr>
<td>{bday.name}</td>
<td>{bday.date}</td>
</tr>
);
}
</table>
I suggest you read more about table elements in html.
One way of presenting your data may be as follows: http://codepen.io/PiotrBerebecki/pen/jrVkdO
Here is the code for the React component class:
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
birthdays: {
'January': [{
name: 'Mike',
date: '1/14/90'
}, {
name: 'Joe',
date: '1/7/92'
}],
March: [{
name: 'Mary',
date: '3/7/88'
}]
}
}
}
render() {
console.log();
return (
<table>
<thead>
<th>
<td>Month</td>
<td>Name</td>
<td>Birthday</td>
</th>
</thead>
<tbody>
{Object.keys(this.state.birthdays).map((month, key) => {
return (
<tr key={key}>
{this.state.birthdays[month].map((person, index) => {
return (
<tr key={String(key) + String(index)}>
<td>{month}</td>
<td>{person.name}</td>
<td>{person.date}</td>
</tr>
);
})}
</tr>
)
})}
</tbody>
</table>
);
}
}
ReactDOM.render(
<App />,
document.getElementById('container')
);
I am attempting to load data into a table using react, and I got to the point where the data does exist when attempting to render the fragment, however, it doesn't seem to actually update the dom and render (I tested and know the data DOES in fact exist when it runs the fragment in the {} block under tbody. Any help would be awesome, thank you.
import React from 'react';
class InvoicePickTable extends React.Component {
constructor(props) {
super(props);
this.state = {invoices:[]};
}
getInvoiceData(){
return new Promise(function(resolve, reject) {
console.log("hi");
resolve([{number: "1"},{number: "2"},{number: "3"}]);
})
}
componentDidMount() {
const self = this;
self.getInvoiceData()
.then((response) => {
self.setState({invoices: response});
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<table>
<thead>
<tr>
<th>Invoice #</th>
<th>Task Price</th>
<th>Balance</th>
<th>Task Name</th>
</tr>
</thead>
<tbody>
{
this.state.invoices.forEach(function (invoice) {
console.log("in");
console.log(invoice);
return (
<tr>
<td>{invoice.number}</td>
</tr>
);
})
}
</tbody>
</table>
);
}
}
export default InvoicePickTable;
Use
this.state.invoices.map(invoice => <tr><td>{invoice.number}</td></tr>)
in your render function. Array.forEach does not actually return an array
I fixed it, you need to use MAP not FOR EACH, hope this helps someone!
import React from 'react';
class InvoicePickTable extends React.Component {
constructor(props) {
super(props);
this.state = {invoices:[]};
}
getInvoiceData(){
return new Promise(function(resolve, reject) {
console.log("hi");
resolve([{number: "1"},{number: "2"},{number: "3"}]);
})
}
componentDidMount() {
const self = this;
self.getInvoiceData()
.then((response) => {
self.setState({invoices: response});
})
.catch((err) => {
console.error(err);
});
}
render() {
return (
<table>
<thead>
<tr>
<th>Invoice #</th>
<th>Task Price</th>
<th>Balance</th>
<th>Task Name</th>
</tr>
</thead>
<tbody>
{
this.state.invoices**.map**(function (invoice) {
console.log("in");
console.log(invoice);
return (
<tr **key = {invoice.number}**>
<td>{invoice.number}</td>
<td>1231</td>
<td>4</td>
<td>A Task</td>
</tr>
);
})
}
</tbody>
</table>
);
}
}
export default InvoicePickTable;
here is a good example of rendering the data with ajax and map function.