Reputation: 59
I have set React state to data from an API
this.setState({loan: response.data})
response.data is a nested object
{
application: {
amount: 20,
interest: 10,
guarantor: {
firstName: "John",
lastName: "Doe"
}
},
userId: "123"
}
Normally inside the render function i can access
<p>{this.state.loan.userId}</p>
<p>{this.state.loan.application.amount}</p>
<p>{this.state.loan.application.guarantor.firstName}</p>
Now I can only access the first child of the loan. Except i practically set the state for each individual item in the object. Note console.log(this.state.loan.application.guarantor)
works fine.
This is the API call
fetch(`http://localhost:8000/api/v1/loans/${this.state.id}`)
.then(res => {
return res.json();
}).then(response => {
this.setState({loan: response.data});
})
.catch(err => console.log(err));
const {loan} = this.state;
<div className="col-md-4">
<h5 className="title">Full Name</h5>
<p>{loan.fullName}</p>
<h5 className="title mt-3">Account Number</h5>
<p>{loan.accountNumber}</p>
<h5 className="title mt-3">Phone Number</h5>
<p>Phone Number</p>
</div>
<div className="col-md-4">
<h5 className="title">Loan Amount</h5>
<p>
{(loan.application.amount).toLocaleString("en-NG", {
style: "currency",
currency: "NGN"
})}
</p>
<h5 className="title mt-3">Interest Rate</h5>
<p>{loan.interestRate}%</p>
<h5 className="title mt-3">Duration</h5>
<p>{loan.duration} Months</p>
</div>
The response from API call
{
"application": {
"guarantor1": {
"fullName": "Ayebakuro Ombu",
"residentialAddress": "30 Udengs Eradiri Avenue Off Azikoro Village Road",
"occupation": "Accountant",
"netIncome": "50000",
"placeOfWork": "Dreamworld",
"employer": "Ayebakuro Ombu",
"relationship": "Employer",
"bvn": "0101010101",
"bank": "GTBank",
"accountNumber": "10101010101",
"phoneNumber": "010101010101"
},
"guarantor2": {
"fullName": "Ayebakuro Ombu",
"residentialAddress": "B48 Copa Cobana Estate, Wumba, Lokogoma",
"occupation": "business man",
"netIncome": "500000",
"placeOfWork": "Dreamworld",
"employer": "SafeScrow Tech",
"relationship": "Employer",
"bvn": "0101010101",
"bank": "GTBank",
"accountNumber": "0101010101",
"phoneNumber": "0101010101"
},
"mode": {
"name": "DreamWorld Savings And Loans Ltd",
"address": "30 Udengs Eradiri Avenue Off Azikoro Village Road",
"netIncome": "50000"
},
"bankDetails": {
"bank": "Parallex Bank",
"accountNumber": "0101010101",
"bvn": "0101010101"
},
"amount": 200000,
"number": "25642",
"date": "2019-03-22T02:37:58.069Z",
"purpose": "For debt payment"
},
"approval": {
"amount": 0,
"status": "Pending"
},
"issue": {
"status": false
},
"payment": {
"schedule": [],
"completed": false
},
"_id": "5c944a86abf7ea09c40301e5",
"accountNumber": "1000000002",
"fullName": "Ayebakuro Ombu",
"type": "Business",
"duration": 5,
"interestRate": 10,
"__v": 0
}
The error: LoanPage.js:61 Uncaught TypeError: Cannot read property 'amount' of undefined at LoanPage.render (LoanPage.js:61)
Logging this.state.loan.application.amount logs correctly
Upvotes: 0
Views: 374
Reputation: 1790
When a component is rendered (like the following code), React calls the render
method of corresponding component immediately.
ReactDom.render(<LoanPage />, element);
Event if you were to execute a asynchronous fetch
in constructor, or componentWillMount
method, that wouldn't prevent the React system from executing render.
This is how you should approach this problem. In constructor / componentWillMount, you should set this.state.loading = true
, and then fire the fetch call. In the .then
part of fetch call, setState to clear the loading flag like this:
this.setState({
loading: false,
loan: response.data
});
The render
method of LoanPage can now benefit from the knowledge of 'fetch call in progress' like this:
render() {
if(this.state.loading) {
return (<h3>Loading...</h3>);
}
return (
<div> Loan amount is {this.state.loan.application.amount} </div>
);
}
You can change the first part of render (in if condition) to display a spinner or some equivalent. You should change the second part to render everything that you are rendering now.
Upvotes: 2