I fetch an api on componentDIdMount() then store the json to a state then I pass that state as a prop, I have no problem showing the data except on arrays.
<Component details={this.state.details} />
json:
{
"adult": false,
"backdrop_path": "/qonBhlm0UjuKX2sH7e73pnG0454.jpg",
"belongs_to_collection": null,
"budget": 90000000,
"genres": [
{
"id": 28,
"name": "Action"
},
{
"id": 878,
"name": "Science Fiction"
},
{
"id": 35,
"name": "Comedy"
},
{
"id": 10751,
"name": "Family"
}
]
}
then I try to map the genres:
<div className={style.genre}>
{details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
</div>
But then I get Cannot read property 'map' of undefined, I don't know why this is happening because I'm able to do details.budget
It's trying to read data before you get the result from api.
so write the map function as
{details&&details.genres&&details.genres.map(g => (
<span key={g.id}>{g.name}</span>
))}
In react Initially when component is mounted, render() function is called and then componenentDidMount() is called in which you fetch data. So Initially details is empty. So you need to write the condition.
Related
I have a simple component tree:
RecipeFeed which renders Recipe
In my RecipeFeed component I map an an array of recipe objects to render a Recipe component for each recipe object in the array
const renderRecipes = () => {
if (recipes) {
return (
<div>
{recipes.map(recipe =>
<Recipe
key={recipe.id}
recipe={recipe}
ingredients={recipe.ingredients}/>
)}
</div>
)
} else {
return (
<div>
....loading
</div>
)
}
}
My Recipe component is similar:
const renderIngredients = () => {
if (props.ingredients) {
props.ingredients.map(ingredient => {
console.log(ingredient.name)
<div>{ingredient.name}</div>
})
}
}
return (
<div>
<div>{props.recipe.name}</div>
{renderIngredients()}
</div>
)
}
My recipe.name is OK, and renders to the DOM. However, although my ingredients are defined in the console.log in Recipe.jsx, Nothing is rendered to the screen.
I believe it must have something to do with the shape of the data and the way I am trying to access the value, but I am confused why it appears to be okay when inspecting the console log - right value type of string, no errors, etc.
The json data looks like this
"data": [
{
"id": 2,
"name": "pizza",
"ingredients": [
{
"id": 5,
"name": "dough",
"food_group": "grain",
"created_at": "2022-03-08T04:39:41.334Z",
"updated_at": "2022-03-08T04:39:41.334Z"
},
{
"id": 6,
"name": "sauce",
"food_group": "vegetable",
"created_at": "2022-03-08T04:40:11.684Z",
"updated_at": "2022-03-08T04:40:11.684Z"
},
{
"id": 7,
"name": "cheese",
"food_group": "dairy",
"created_at": "2022-03-08T04:40:33.032Z",
"updated_at": "2022-03-08T04:40:33.032Z"
}
],
"recipe_ingredients": [
{
"id": 3,
"recipe_id": 2,
"ingredient_id": 5,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.482Z",
"updated_at": "2022-03-08T04:41:06.482Z"
},
{
"id": 4,
"recipe_id": 2,
"ingredient_id": 6,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.484Z",
"updated_at": "2022-03-08T04:41:06.484Z"
},
{
"id": 5,
"recipe_id": 2,
"ingredient_id": 7,
"quantity": null,
"measurement_unit": null,
"created_at": "2022-03-08T04:41:06.485Z",
"updated_at": "2022-03-08T04:41:06.485Z"
}
]
}
You've got numerous mistakes here.
First: the second return statement is not inside a function. It's not part of the renderIngredients function -- which it can't be anyway, since then it would call itself. (Yes, recursion is legit in javascript, but this case would be perfectly circular and thus break.)
(On second glance, this is maybe because you forgot to include the beginning of the Recipe component, but we're not mind readers here.)
Second: your renderIngredients function won't accomplish anything, because: (1) it does not return anything, and (2) the map inside it also doesn't return anything. This is the direct answer to your question, "Why is my component not rendering a prop value?": it's because you haven't chosen to return anything, either with the return keyword, or by using the short arrow form: () => returnValue.
Third: the signature of your Recipe component is bad:
<Recipe
key={recipe.id}
recipe={recipe}
ingredients={recipe.ingredients}
/>
Why are you passing both the recipe and the ingredients separately? The component can access the ingredients through the recipe. You've set it up so that the same data has to be fed into it twice.
Fourth: the names of component functions must start with a capital letter so that the React framework recognizes them as custom components instead of native HTML components. And don't name your component "renderRecipes", name it "RecipeList".
I am mapping over some data that I am getting from a api however when i try to add the filter function i get 'currencyData.includes is not a function'
I have also tried just hard coding the array but it also still doesnt work?
I have a loading state for when i fetch data from the api which holds code from being run but i have removed it from this example as its not getting data from the api below.
The simplified version is here...
ARRAY
var items = [
{
"id": 1,
"productName": "shoes",
"productIdentifier": "CL001",
"productDescription": "adidas kicks boir",
"productPrice": 2000,
"productStock": 200,
"created_at": "2020-51-28",
"updated_at": null
},
{
"id": 2,
"productName": "burger",
"productIdentifier": "FD001",
"productDescription": "charsiu berger",
"productPrice": 2000,
"productStock": 200,
"created_at": "2020-51-28",
"updated_at": null
}
]
return(
{items.filter(currencyInfo => currencyInfo.includes("FD001")).map((value, index) => {
console.log(value)
return(
<h1 key={index}>{value}</h1>
)
})}
)
currencyInfo is not an array, you can not call includes on it
Here is my suggestion:
return(
{items.filter(currencyInfo => currencyInfo.productIdentifier === "FD001").map((value, index) => {
console.log(value)
return(
<h1 key={index}>{value}</h1>
)
})}
)
More about includes()
The includes() method determines whether an array includes a certain value among its entries, returning true or false as appropriate. Check this Doc
But in items.filter(currencyInfo => currencyInfo.includes("FD001")), type of currencyInfo isn't array but object.
So you should use currencyInfo.productIdentifier.includes()
I'm trying to access data further down into my JSON file. I am able to easily access data in the first two data sets in rows and area:
data.json
"rows": [
{
"uid":"001",
"type": "Lorem ipsum",
"area": [
{
"name": "London",
"number": "12345",
"wait": {
"start": {
"start_time": 1585129140,
"delay": 300
},
"end": {
"end_time": 1585130100,
"delay": 300
}
},
"in": 1585129140,
"out": 1585130100,
},
However when I try to access the data under wait which includes this block:
"wait": {
"start": {
"start_time": 1585129140,
"delay": 300
},
"end": {
"end_time": 1585130100,
"delay": 300
}
},
No data is getting returned on screen from my jsx file, but it is available in the console log
TimeTracker.jsx
const TimeTracker = (props) => {
const trainTime = useState(props.data);
console.log(props.data);
return (
<>
<div className={style.timeLabel}>
<div className={style.startTime}>{trainTime.start_time}</div>
<div className={style.endTime}></div>
</div>
</>
)
};
export default TimeTracker;
console.log
wait:
start:
start_time: 1585129140
delay: 300
__proto__: Object
end:
end_time: 1585130100
delay: 300
__proto__: Object
__proto__: Object
I've used the same pattern for passing props in other components and it works fine on the first two levels so I don't understand why it's not working. How do I get data from further in this JSON?
useState returns a tuple with the object and a function to set the value on the object. You probably need to change your component to something like this:
const TimeTracker = (props) => {
const [trainTime, setTrainTime] = useState(props.data);
console.log(props.data);
return (
<>
<div className={style.timeLabel}>
<div className={style.startTime}>{trainTime.start_time}</div>
<div className={style.endTime}></div>
</div>
</>
)
};
export default TimeTracker;
A nested property can not be accessed by one level of a.b so instead of
<div className={style.startTime}>{trainTime.start_time}</div>
it should be
<div className={style.startTime}>{trainTime.wait.start.start_time}</div>
I've defined an empty array in the react component constructor which I wish to assign with json response data from an API call
class Grid extends Component {
constructor(props) {
super(props);
this.state = {
blogs : []
};
}
I call the componentDidMount method to load data on this component where I'm using setState to assign values
componentDidMount(){
Axios.get("/api/blogs").then(response => {
const blogData = response.data;
this.setState({blogs: blogData.data});
});
}
The JSON response data is as follows (Laravel collection resource with meta and links)
{
"data": [
{
"title": "Experiments in DataOps",
"status": true,
"publish_date": "2020-01-29",
"slug": "experiments-in-dataops"
},
{
"title": "What is it about certifications anyway?",
"status": true,
"publish_date": "2020-01-29",
"slug": "what-is-it-about-certifications-anyway"
}
],
"links": {
"self": "link-value",
"first": "http://adminpanel.test/api/blogs?page=1",
"last": "http://adminpanel.test/api/blogs?page=1",
"prev": null,
"next": null
},
"meta": {
"current_page": 1,
"from": 1,
"last_page": 1,
"path": "http://adminpanel.test/api/blogs",
"per_page": 15,
"to": 2,
"total": 2
}
}
the blogs array however remains undefined when I'm calling it later in populating the grid data
<BootstrapTable data={this.blogs} version="4" striped hover pagination search options={this.options}>
I get an empty table, calling console.log on blogs and this.blogs reveals it's undefined.
I'm using reactstrap and react-bootstrap-table for this grid component.
You are not accessing the state correctly to access the blogs use this.state.blogs:
<BootstrapTable data={this.state.blogs} version="4" striped hover pagination search options={this.options}>
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
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.