Edit and Delete functionality is not working as Excepted - reactjs

I Have a form,when I submit the details of that form,It will redirect to next page and date will display there. There itself I have two buttons to Edit and Delete.That two Functionality is not working..
Can you help me in this?? Thanks in Advance..
import React, { Component } from 'react';
import axios from "axios";
import {withRouter} from 'react-router';
class App extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
objEdit:obj
}
}
// handleInput(e){
// this.state.objEdit[e.target.name] = e.target.value
// this.setState({objEdit:this.state.objEdit})
// }
handleInput(e){
this.setState({
objEdit:{...this.state.objEdit, [e.target.name] : e.target.value}
})
}
updateUser = () =>{
console.log(this.state.objEdit)
const objEdit = {...this.state.objEdit, id: null};
axios.post("http://localhost:3000/data/", objEdit).then(()=>{
this.props.history.push('/Form')
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="col-sm-4">
<form>
<div className="form-group">
<label htmlFor="fname">First Name</label>
<input type="text" className="form-control" name="fname" placeholder="Enter First Name" value={this.state.objEdit.fname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="lname">Last Name</label>
<input type="text" className="form-control" name="lname" placeholder="Enter First Name" value={this.state.objEdit.lname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="tel">Tel</label>
<input type="text" className="form-control" name="tel" placeholder="Tel" value={this.state.objEdit.tel} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input type="text" className="form-control" name="address" placeholder="Enter First Name" value={this.state.objEdit.address} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="fname">City</label>
<input type="text" className="form-control" name="city" placeholder="Enter First Name" value={this.state.objEdit.city} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="state">State</label>
<input type="text" className="form-control" name="state" placeholder="Enter First Name" value={this.state.objEdit.state} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="zip">Zip</label>
<input type="text" className="form-control" name="zip" placeholder="Enter First Name" value={this.state.objEdit.zip} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<button type="button" className="btn btn-primary" onClick={this.updateUser}>Submit</button>
</form>
</div>
</div>
// </div>
)
}
}
export default withRouter(App);
Here my Edit and Delete Function are there.I have two buttons to Edit and Delete.That two Functionality is not working..
let obj={
fname:"",
lname:"",
tel:"",
address:"",
city:"",
state:"",
zip:'',
id:''
}
import React, { Component } from 'react'
import axios from "axios";
import { Link } from 'react-router-dom';
class Form extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
}
}
componentDidMount(){
fetch('http://localhost:3000/data')
.then(response => response.json())
.then(user => this.setState({data:user}))
}
editUser=(obj,index)=>{
console.log(obj,index)
this.setState({objEdit:obj})
}
deleteUser=(i)=>{
console.log("deleteUser called",i)
axios.delete("http://localhost:3000/data/"+this.state.data[i].id).then(res=>{
console.log()
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="row">
<div className="col-sm-8">
<table className="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Tel</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{this.state.data.map((obj,i)=>{
return <tr key={i}>{Object.keys(obj).map((property)=>{
return <td key={property}>{obj[property]}</td>
})}<td><button onClick={()=>{this.editUser(obj,i)}} className="btn btn-info">Edit</button></td>
<td><button onClick={()=>{this.deleteUser(i)}} className="btn btn-danger">Delete</button></td></tr>
})}
</tbody>
<Link to ="/"> <button type="button" className="btn btn-primary" >Back</button></Link>
</table>
</div>
</div>
</div>
)
}
}
export default Form;

Delete is a simple. As you are iterating over local state for your data, you need to delete the data from local state when you get success from delete axios call,
deleteUser=(i)=>{
console.log("deleteUser called",i)
axios.delete("http://localhost:3000/data/"+this.state.data[i].id).then(res=>{
console.log("success");
this.state.data.splice(i,1); //This will delete data fro local state.
}).catch((error)=>{
console.log("Error Occured")
})
}
Now for update you cannot directly setState state from some other component where you don't have any relation.
this.setState({objEdit:obj})
This cannot be done, because objEdit is from App component. You don't have any relation between App and Form component.
One way is you can pass obj like, (Just an idea)
editUser=(obj,index)=>{
console.log(obj,index)
this.props.history.push({
pathname:"/", //probablt this is for App component
state: {objEdit : JSON.stringify(obj)}
})
}
Of course you need to wrap Form component using withRouter,
export default withRouter(Form)
Now in App component you can get this data in componentDiMount,
componentDidMount(){
let objEdit = JSON.parse(this.props.location.state.objEdit)
if(objEdit){
this.setState({objEdit})
}
}
Now you will get pre-filled values in your form, you just need to handle the update button functionality.
In your update function you are adding id manually,
const objEdit = {...this.state.objEdit, id: null};
But in this case you need to have check that if objEdit alreay have id, if yes then you need to hit the update API call.

Related

Error: Too many re-renders. React limits the number of renders to prevent an infinite loop. Error in Contact.js file

I am trying to get my form populated when I click on the edit pencil button but I keep getting the error 'to many re-renders'. Anyone can help here? down below my two files
I am using firebase to pull out data from a collection and basically I am trying to build a simple application where you can display data from that database and then eventually edit it or delete it
Contact.js
import React, { useState, useEffect } from "react";
import ContactForm from "./ContactForm";
import firebaseDb from "../firebase";
const Contacts = () => {
var [learnerObjects, setLearnerObjects] = useState({});
var [currentId, setCurrentId] = useState("");
useEffect(() => {
firebaseDb.child("learners").on("value", (snapshot) => {
if (snapshot.val() != null)
setLearnerObjects({
...snapshot.val(),
});
});
}, []);
const addOrEdit = (obj) => {
firebaseDb.child("learners").push(obj, (err) => {
if (err) console.log(err);
});
};
return (
<>
<div class="jumbotron jumbotron-fluid">
<div class="container">
<h1 class="display-4 text-center">Contact Register</h1>
</div>
</div>
<div className="row">
<div className="col-md-5">
<ContactForm
{...setCurrentId({ addOrEdit, currentId, learnerObjects })}
/>
</div>
<div className="col-md-7">
<table className="table table-borderless table-stripped">
<thead className="thead-light">
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>email</th>
<th>score</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
{Object.keys(learnerObjects).map(id => {
return (
<tr key={id}>
<td>{learnerObjects[id].firstName}</td>
<td>{learnerObjects[id].lastName}</td>
<td>{learnerObjects[id].email}</td>
<td>{learnerObjects[id].score}</td>
<td>
<a
className="btn text-primary"
onClick={() => {
setCurrentId(id)
}}
>
<i className="fas fa-pencil-alt"></i>
</a>
<a className="btn text-danger">
<i className="fas fa-trash-alt"></i>
</a>
</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
</>
);
};
export default Contacts;
ContactForm.js
import React, { useEffect, useState } from "react";
const ContactForm = (props) => {
const initialFieldValues = {
firstName: "",
lastName: "",
email: "",
score: 0,
};
var [values, setValues] = useState(initialFieldValues);
useEffect(() => {
if (props.currentId === '')
setValues({
...initialFieldValues,
});
else
setValues({
...props.learnerObjects[props.currentId],
});
}, [props.currentId, props.learnerObjects]);
const handleInputChange = (e) => {
var { name, value } = e.target;
setValues({
...values,
[name]: value,
});
};
const handleFormSubmit = (e) => {
e.preventDefault();
props.addOrEdit(values);
};
return (
<form autoComplete="off" onSubmit={handleFormSubmit}>
<div className="form-group input-group">
<div className="input-group-prepend">
<div className="input-group-text">
<i className="fas fa-user"></i>
</div>
</div>
<input
className="form-control"
placeholder="first name"
name="firstName"
value={values.firstName}
onChange={handleInputChange}
/>
</div>
<div className="form-row">
<div className="form-group input-group col-md-6">
<div className="input-group-prepend">
<div className="input-group-text">
<i className="fas fa-user"></i>
</div>
</div>
<input
className="form-control"
placeholder="last name"
name="lastName"
value={values.lastName}
onChange={handleInputChange}
/>
</div>
<div className="form-group input-group col-md-6">
<div className="input-group-prepend">
<div className="input-group-text">
<i className="fas fa-user"></i>
</div>
</div>
<input
className="form-control"
placeholder="email"
name="email"
value={values.email}
onChange={handleInputChange}
/>
</div>
</div>
<div className="form-group">
<textarea
className="form-control"
placeholder="score"
name="score"
value={values.score}
onChange={handleInputChange}
/>
</div>
<div className="form-group">
<input
type="submit"
value="Save"
className="btn btn-primary btn-block"
/>
</div>
</form>
);
};
export default ContactForm;
You are calling {...setCurrentId({ addOrEdit, currentId, learnerObjects })} on render.
You are updating the useState hook on render, which results in a rerender during which you are again updating the hook resulting in an infinity loop.
You need to update the state somewhere that is not executed on every render.

After Button Clicked, Unable to Edit the Data

I have a form, After filling the Details and clicking on Submit Button, It will redirect to the next page, There I Have One Edit button to edit the fields. But Edit functionality is working as Excepted.
Can Anyone help me in this?? Thanks in Advance
import React, { Component } from 'react';
import axios from "axios";
import {withRouter} from 'react-router';
class App extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
objEdit:obj
}
}
handleInput(e){
this.setState({
objEdit:{...this.state.objEdit, [e.target.name] : e.target.value}
})
}
updateUser = () =>{
console.log(this.state.objEdit)
const objEdit = {...this.state.objEdit, id: null};
axios.post("http://localhost:3000/data/", objEdit).then(()=>{
this.props.history.push('/Form')
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="col-sm-4">
<form>
<div className="form-group">
<label htmlFor="fname">First Name</label>
<input type="text" className="form-control" name="fname" placeholder="Enter First Name" value={this.state.objEdit.fname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="lname">Last Name</label>
<input type="text" className="form-control" name="lname" placeholder="Enter First Name" value={this.state.objEdit.lname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="tel">Tel</label>
<input type="text" className="form-control" name="tel" placeholder="Tel" value={this.state.objEdit.tel} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input type="text" className="form-control" name="address" placeholder="Enter First Name" value={this.state.objEdit.address} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="fname">City</label>
<input type="text" className="form-control" name="city" placeholder="Enter First Name" value={this.state.objEdit.city} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="state">State</label>
<input type="text" className="form-control" name="state" placeholder="Enter First Name" value={this.state.objEdit.state} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="zip">Zip</label>
<input type="text" className="form-control" name="zip" placeholder="Enter First Name" value={this.state.objEdit.zip} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<button type="button" className="btn btn-primary" onClick={this.updateUser}>Submit</button>
</form>
</div>
</div>
)
}
}
export default withRouter(App);
let obj={
fname:"",
lname:"",
tel:"",
address:"",
city:"",
state:"",
zip:'',
id:''
}
Here my Edit and Delete Function are there.I have two buttons to Edit and Delete.That two Functionality is not working..
import React, { Component } from 'react'
import axios from "axios";
import { Link } from 'react-router-dom';
import App from './App';
class Form extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
}
}
componentDidMount(){
fetch('http://localhost:3000/data')
.then(response => response.json())
.then(user => this.setState({data:user}))
}
editUser=(obj,index)=>{
console.log(obj,index)
this.setState({objEdit:obj})
}
deleteUser=(i)=>{
console.log("deleteUser called",i)
axios.delete("http://localhost:3000/data/"+this.state.data[i].id).then(res=>{
console.log()
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="row">
<div className="col-sm-8">
<table className="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Tel</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{this.state.data.map((obj,i)=>{
return <tr key={i}>{Object.keys(obj).map((property)=>{
return <td key={property}>{obj[property]}</td>
})}<td><button onClick={()=>{this.editUser(obj,i)}} className="btn btn-info">Edit</button></td>
<td><button onClick={()=>{this.deleteUser(i)}} className="btn btn-danger">Delete</button></td></tr>
})}
</tbody>
<Link to ="/"> <button type="button" className="btn btn-primary" >Back</button></Link>
</table>
</div>
</div>
</div>
)
}
}
export default Form;

when button is clicked, Form is redirected to another page But Data is not updating

I am giving details in the form and clicking the submit button to populate the data in next page..So, on submit button I have given Link to redirect to the next page.In that page the data should display..But before submission of the data, it is redirecting me to next page and the data I am not able to fetch,But once I refresh the page I am able to see the updated value, Same happening with DELETE button also.
Can you help me in this?? Thanks in Advance.
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
objEdit:obj
}
}
handleInput(e){
this.state.objEdit[e.target.name] = e.target.value
this.setState({objEdit:this.state.objEdit})
}
updateUser = () =>{
console.log(this.state.objEdit)
this.state.objEdit.id=null
axios.post("http://localhost:3000/data/",this.state.objEdit).then(res=>{
console.log()
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="col-sm-4">
<form>
<div className="form-group">
<label htmlFor="fname">First Name</label>
<input type="text" className="form-control" name="fname" placeholder="Enter First Name" value={this.state.objEdit.fname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="lname">Last Name</label>
<input type="text" className="form-control" name="lname" placeholder="Enter First Name" value={this.state.objEdit.lname} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="tel">Tel</label>
<input type="text" className="form-control" name="tel" placeholder="Tel" value={this.state.objEdit.tel} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="address">Address</label>
<input type="text" className="form-control" name="address" placeholder="Enter First Name" value={this.state.objEdit.address} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="fname">City</label>
<input type="text" className="form-control" name="city" placeholder="Enter First Name" value={this.state.objEdit.city} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="state">State</label>
<input type="text" className="form-control" name="state" placeholder="Enter First Name" value={this.state.objEdit.state} onChange={(e)=>{this.handleInput(e)}}/>
</div>
<div className="form-group">
<label htmlFor="zip">Zip</label>
<input type="text" className="form-control" name="zip" placeholder="Enter First Name" value={this.state.objEdit.zip} onChange={(e)=>{this.handleInput(e)}}/>
</div>
{/* <button onClick={this.updateUser} type="button">Add</button> */}
<Link to="/Form"> <button type="button" className="btn btn-primary" onClick={this.updateUser}>Submit</button></Link>
</form>
</div>
</div>
// </div>
)
}
}
let obj={
fname:"",
lname:"",
tel:"",
address:"",
city:"",
state:"",
zip:'',
id:''
}
class Form extends Component {
constructor(props) {
super(props)
this.state = {
data:[],
}
}
componentDidMount(){
fetch('http://localhost:3000/data')
.then(response => response.json())
.then(user => this.setState({data:user}))
}
editUser=(obj,index)=>{
console.log(obj,index)
this.setState({objEdit:obj})
}
deleteUser=(i)=>{
console.log("deleteUser called",i)
axios.delete("http://localhost:3000/data/"+this.state.data[i].id).then(res=>{
console.log()
}).catch((error)=>{
console.log("Error Occured")
})
}
render() {
return (
<div>
<div className="row">
<div className="col-sm-8">
<table className="table">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Tel</th>
<th>Address</th>
<th>City</th>
<th>State</th>
<th>Zip</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{this.state.data.map((obj,i)=>{
return <tr key={i}>{Object.keys(obj).map((property)=>{
return <td key={property}>{obj[property]}</td>
})}<td><button onClick={()=>{this.editUser(obj,i)}} className="btn btn-info">Edit</button></td>
<td><button onClick={()=>{this.deleteUser(i)}} className="btn btn-danger">Delete</button></td></tr>
})}
</tbody>
</table>
</div>
</div>
</div>
)
}
}
export default Form;
When I Click on SUbmit Button, It should redirect me to next page with updated values And edit and delete button also should work properly without refreshing the page.
You need to use the history object of react-router to goto the next page after you get the response form the api call. When you use Link, it redirects to the page directly.
Also,
handleInput(e){
this.state.objEdit[e.target.name] = e.target.value
this.setState({objEdit:this.state.objEdit})
}
(and)
this.state.objEdit.id=null
Why are you even doing this? You shouldn't be mutating the state object.
handleInput(e){
this.setState({
objEdit:{...this.state.obj, [e.target.name] : e.target.value}
})
}
import {withRouter} from 'react-router';
.
.
.
updateUser = () =>{
console.log(this.state.objEdit)
// this.state.objEdit.id=null
const objEdit = {...this.state.objEdit, id: null};
axios.post("http://localhost:3000/data/", objEdit).then(res=>{
console.log(res)
this.props.history.push('/Form')
}).catch((error)=>{
console.log("Error Occured")
})
}
.
.
.
// remove the Link.
<button
type="button"
className="btn btn-primary"
onClick={this.updateUser}
>
Submit
</button>
.
.
.
export default withRouter(App)
You can do the same for others.

Mapping objects in an array to table in React

Could you please help me in passing objects in a state array to the table using map method in react?
I can able to push the object into the invoices array but I can't map into the table.
Please suggest if any other method is possible.
Please neglect the below comments as I am going repeat the above context.
Could you please help me in passing objects in a state array to the table using map method in react?
I can able to push the object into the invoices array but I can't map into the table.
Please suggest if any other method is possible.
import React, { Component } from 'react';
class Form extends Component {
constructor(props) {
super(props);
this.state = {
company: "",
address: "",
zip: "",
date: "",
description: "",
unit: "",
quantity: "",
invoices: []
};
}
handleChange = (e) => {
e.preventDefault();
this.setState({ [e.target.name]: e.target.value })
};
handleSubmit = (e) => {
e.preventDefault();
this.state.invoices.push({
description: this.state.description,
unit: this.state.unit,
quantity: this.state.quantity
});
//console.log(this.state.invoices[].description);
};
render() {
const hrStyle = {
border: '5px solid rgb(23, 162, 184)'
};
const list = this.state.invoices.map((invoice, index) => {
return (
<tr key={index}>
<td>{invoice[index].description}</td>
<td>{invoice[index].unit}</td>
<td>{invoice[index].quantity}</td>
<td>{invoice[index].unit * invoice[index].quantity}</td>
</tr>
)
});
return (
<React.Fragment>
<div className='col-12 col-lg-6'>
<div className="jumbotron">
<form>
<label><h4>Billed To: </h4></label><br />
<div className="form-group">
<label>Company Name</label>
<input onChange={this.handleChange} className="form-control" type="text" name="company" />
</div>
<div className="form-group">
<label>Address</label>
<input className="form-control" type="text" onChange={this.handleChange} name="address" />
<label>Zip Code</label>
<input className="form-control" type="number" onChange={this.handleChange} name="zip" /></div>
<div className="form-group">
<label>Date</label>
<input className="form-control" type="date" onChange={this.handleChange} name="date" />
</div>
</form>
<form onSubmit={this.handleSubmit}>
<label><h4>Invoice: </h4></label><br />
<div className="form-group">
<label>Description</label>
<input className="form-control" type="text" onChange={this.handleChange} name="description" />
</div>
<div className="form-group">
<label>Unit Price</label>
<input className="form-control" type="number" onChange={this.handleChange} name="unit" />
<label>Quantity</label>
<input className="form-control" type="number" onChange={this.handleChange} name="quantity" />
</div>
<button className="btn btn-primary btn-sm">Add Invoices</button>
</form>
</div>
</div>
<div className="col-12 col-lg-6">
<div className="container-fluid bg-info text-white">
<div className="row">
<div className="col text-left">
<p>Your Company Name</p>
<h2>Invoice</h2>
</div>
<div className="col text-right">
<p>22 Yusen St</p><br />
<p>Auburn</p><br />
<p>NSW</p>
</div>
</div>
</div>
<div className="container-fluid">
<div className="row">
<div className="col-4">
<p>{this.state.company}</p>
<p>{this.state.address}</p>
<p>{this.state.Zip}</p>
</div>
<div className="col-4">
<div>
<h5>Invoive number</h5>
<p>{Math.floor((Math.random() * 100) + 1)}</p>
</div>
<div>
<h5>Date</h5>
<p>{this.state.date}</p>
</div>
</div>
<div className="col-4">
<div>
<h5>Invoice Totals</h5>
<p>$2587.35</p>
</div>
</div>
</div>
</div>
<hr style={hrStyle} />
<div className="Invoices">
<table className="table">
<thead>
<tr>
<th>Description</th>
<th>Unit Price</th>
<th>Quantity</th>
<th>Total</th>
</tr>
</thead>
<tbody>
{list}
</tbody>
</table>
</div>
</div>
</React.Fragment>
);
}
}
export default Form;
One possible issue that I can see is not properly setting state in handleSubmit. You should do something like this:
handleSubmit = (e) => {
e.preventDefault();
const copyStateInvoices = [...this.state.invoices]
copyStateInvoices.push({
description: this.state.description,
unit: this.state.unit,
quantity: this.state.quantity
})
this.setState({
invoices: copyStateInvoices,
})
//console.log(this.state.invoices[].description);
};
Component's state is immutable and if you try to change the values in state it will happen but react will not respect that. Read more about these fundamentals on react main website.
Thanks
Try with following changes.
Needs change in pushing objects to an array state
To push objects or values or numbers in react you should do something like below. The recommended approach to push values to an array in React is using prevState
handleSubmit = (e) => {
e.preventDefault();
this.setState(prevState => ({
invoices: [...prevState.invoices, {
description: this.state.description,
unit: this.state.unit,
quantity: this.state.quantity
}]
}));
};
And in .map you no need to do invoice[index] to get the value because you are doing .map on array so it gives you each object in loop so you can directly access invoice.unit directly and invoice[index].unit is not needed and not correct in loop
const list = this.state.invoices.map((invoice, index) => {
return (
<tr key={index}>
<td>{invoice.description}</td>
<td>{invoice.unit}</td>
<td>{invoice.quantity}</td>
<td>{invoice.unit * invoice.quantity}</td>
</tr>
)
});

TypeError: Cannot read property 'title' of undefined in React

I have an object which is passed ti the n=modal component in the form
data =[
{
_id: "5ba241b4efa8da2f1464ca81",
title: "Zero To One",
author: "Peter Thiel",
isbn: 1279031943,
__v: 0
}
]
I want to access the title key on the data but when I do I get a TypeError.
I tried converting it to a string using JSON.stringigy() but I still got the same error.
I am trying to access the data within the modal so when the modal opens, the value of the inputs in the modal is set with the data from the Table so I can easily edit them
When I console.log(data[0]) I get the items in the object but as soon as I log console.log(data[0].title) the TypeError appears again.
import React, { Component } from 'react';
import './Update.css';
import Search from '../Search/Search';
// import Modal from './Modal/Modal';
const Table = ({ data, openBookDetails }) => (
<table class="table table-hover">
<thead>
<tr class="table-primary">
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">No. Of Copies</th>
</tr>
</thead>
<tbody>
{data.map(row =>
<TableRow key={row.id} row={row} openBookDetails={openBookDetails}/>
)}
</tbody>
</table>
)
const TableRow = ({ row, openBookDetails }) => (
<tr class="table-light" onClick={openBookDetails}>
<th scope="row" >{row.title}</th>
<td >{row.author}</td>
<td >{row.isbn}</td>
<td >24</td>
</tr>
)
const Modal = ({ closeBookDetails, isBookDetailsOpen, children, data }) => {
const showHideClassName = isBookDetailsOpen ? 'modal display-block' : 'modal display-none';
console.log(data[0].title);
return (
<div className={showHideClassName}>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Update Book</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<div class="form-group">
<label htmlFor="title">Title</label>
<input type="text" class="form-control" id="title" aria-describedby="title" placeholder="Enter title of book" value={data}/>
</div>
<div class="form-group">
<label htmlFor="author">Author</label>
<input type="text" class="form-control" id="author" aria-describedby="author" placeholder="Enter author name" value={data}/>
</div>
<div class="form-group">
<label htmlFor="isbn">ISBN</label>
<input type="number" min="0" class="form-control" id="isbn" aria-describedby="isbn" placeholder="Enter ISBN number" value={data.title}/>
</div>
<div class="form-group">
<label for="copies">Number of Copies</label>
<input type="number" min="0" class="form-control" id="copies" aria-describedby="copies" placeholder="Enter the number of copies" value={data.title}/>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary">Save changes</button>
<button type="button" class="btn btn-secondary" data-dismiss="modal" onClick={closeBookDetails}>Close</button>
</div>
</div>
</div>
{children}
</div>
);
};
class Update extends Component{
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: [],
isBookDetailsOpen: false,
};
this.setTableData = this.setTableData.bind(this);
this.openBookDetails = this.openBookDetails.bind(this);
this.closeBookDetails = this.closeBookDetails.bind(this);
}
setTableData(searchedBook){
this.setState({searchedBooks: searchedBook})
console.log(this.state.searchedBooks)
}
openBookDetails(){
console.log('openBookDetails')
this.setState({ isBookDetailsOpen: true})
}
closeBookDetails(){
this.setState({ isBookDetailsOpen: false})
}
render(){
return(
<div>
<Search state={this.state} setTableData={this.setTableData} />
<Table data={this.state.searchedBooks} openBookDetails={this.openBookDetails}/>
<Modal data={this.state.searchedBooks} isBookDetailsOpen={this.state.isBookDetailsOpen} closeBookDetails={this.closeBookDetails} />
{/* <Modal /> */}
</div>
)
}
}
export default Update;
When you pass in data as this.state.searchedBooks you are passing in the defined object, but from your code I can't see where you are actually setting the value to the array listed above. Are you missing part of the code?
Doing a console log on this would not return an error if done as console.log(data[0]) but would return a type error for data[0].title or anything other part of this object.
Can you try something like this:
if(data) {
console.log(data[0].title)
}
So we can tell if the response from the server has arrived. Then maybe this will help to make a further assessment.
Also have you considered using an arrow function within the Update component so you can eliminate the need to bind these functions.
class Update extends Component{
constructor(props) {
super(props);
this.state = {
value: '',
suggestions: [],
setOfAllBooks: [],
searchedBooks: [],
isBookDetailsOpen: false,
};
}
setTableData = (searchedBook) => {
this.setState({searchedBooks: searchedBook})
console.log(this.state.searchedBooks)
}
openBookDetails = () => {
console.log('openBookDetails')
this.setState({ isBookDetailsOpen: true})
}
closeBookDetails = () => {
this.setState({ isBookDetailsOpen: false})
}
render(){
return(
<div>
<Search state={this.state} setTableData={this.setTableData} />
<Table data={this.state.searchedBooks} openBookDetails={this.openBookDetails}/>
<Modal data={this.state.searchedBooks} isBookDetailsOpen={this.state.isBookDetailsOpen} closeBookDetails={this.closeBookDetails} />
{/* <Modal /> */}
</div>
)
}
}
Update:
Is the error thrown at that point or later because in the modal component I do see two references to data.title that would throw an error.
changes these lines as follows:
<div class="form-group">
<label htmlFor="isbn">ISBN</label>
<input type="number" min="0" class="form-control" id="isbn" aria-describedby="isbn" placeholder="Enter ISBN number" value={data[0].title}/>
</div>
<div class="form-group">
<label for="copies">Number of Copies</label>
<input type="number" min="0" class="form-control" id="copies" aria-describedby="copies" placeholder="Enter the number of copies" value={data[0].title}/>
</div>
In this case your input is trying to use a value of data.title which doesn't exist. Not sure if this doesn't work.
I found a work around it.
Inside of the Modal I did
let selectedBookDetails = []
data.map(row => selectedBookDetails = row)
And then I accessed the data like this selectedBookDetails.title, selectedBookDetails.author, selectedBookDetails.isbn
Now the next challenge is getting it to edit.
Thanks everyone for your contribution.
Instead use this:
console.log(data[0]?.title)
That will do the trick.

Resources