this.props.map is not a function in react - javascript

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

Related

Setting default table order in Reactjs

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

Dynamically map data to table using React (without keys)

Im creating a table from JSON data, and main problem is I can't use object keys to map it.
My json something like this:
[{
key: 'val',
key2: 'val',
key3: 'val'
},
{
key: 'val',
key2: 'val',
key3: 'val'
}]
Here is how i implemented header with columns:
class Table extends Component {
render() {
const header = this.props.data.slice(0, 1);
return (<table>
<thead>
<TableHead children={header}/>
</thead>
<tbody>
<TableBody children={this.props.data}/>
</tbody>
</table>)
}
}
export default Table;
class TableHead extends Component {
render() {
return (
<tr>
{this.props.children.map((header) => {
return Object.keys(header).map((el) => {
return <th>{el}</th>
})
})}
</tr>
)
}
}
export default TableHead;
But I can't understand how to map my table body iterating over objects...
I sliced my JSON for header, but I can't do this with data, and my table looks like
class TableBody extends Component {
render() {
const row = this.props.children.map((row) => {
return Object.values(row).map((el,i) => {
if (i%Object.values(row).length===0) {
return <tr><td>{el}</td></tr>
}else{
return <td>{el}</td>
}
})
});
return (
<tbody>
{row}
</tbody>
)
}
}
export default TableBody;
I would extract the keys and re-use when mapping over the rows for the TableBody.
Something like
class Table extends Component {
render() {
const { data } = this.props;
const columns = Object.keys(data[0]);
return (
<table>
<thead>
<TableHead columns={columns} />
</thead>
<tbody>
<TableBody columns={columns} data={data} />
</tbody>
</table>
);
}
}
class TableHead extends Component {
render() {
const { columns } = this.props;
return (
<tr>
{columns.map(header => {
return <th>{header}</th>;
})}
</tr>
);
}
}
class TableBody extends Component {
render() {
const { columns, data } = this.props;
return data.map(row => (
<tr>
{columns.map(cell => (
<td>{row[cell]}</td>
))}
</tr>
));
}
}

Select table cell in react bootstrap table

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>

Why I am getting the table 4 times its size in ReactJs?

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

React not rendering AJAX data

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.

Categories

Resources