Mapping Double nested JSON - Object not allowed in React Child - reactjs

I have a json file that looks like this:
"skills":[
{
"type":"Languages",
"skill":[
{
"name":"Python"
},
{
"name":"Java"
},
{
"name":"JavaScript"
},
{
"name":"HTML"
},
{
"name":"Bash"
},
{
"name":"MySQL"
}
]
},
{
"type": "Flavours",
"skill": [
{
"name":"Reactjs"
},
{
"name":"Angularjs"
},
{
"name":"Agile"
},
{
"name":"Waterfall"
},
{
"name":"Kanban"
}
]
},
{
"type": "Technologies",
"skill": [
{
"name":"Jira"
},
{
"name":" BitBucket"
},
{
"name":"Git"
}
]
}
]
},
And i am trying to render it using a nested mapping function:
var skills = this.props.data.skills.map((skills) =>{
var skill = skills.skill.map(function(skill){
return <li key={skill.name}>{skill}</li>
})
return <ul key={skills.type}>{skills}</ul>
})
However it says "Error: Objects are not valid as a React child (found: object with keys {name}). If you meant to render a collection of children, use an array instead."
So i tried it like this:
var skills = this.props.data.skills.map(function(skills){
var skillsArr = []
var skill = skills.skill.map(function(skill){
var skillArr = []
skillArr.push(<li key={skill.name}>{skill}</li>)
return <span>{skillArr}</span>
})
skillsArr.push(<div key={skills.type}><h3>{skills.type}</h3>
<ul>
{skill}
</ul>
</div>);
return <div>{skillsArr}</div>
})
But this too gives me the exact same error, i dont get what is wrong here because if i do a single mapping of just the skill types it works, it is only when i try to render the inner mapped items does this error occur and break my code
This is how i am calling it btw:
<div className="bars">
<ul className="skills">
{skills}
</ul>
</div>

If we are talking about using React, you should think more about how to organize your code in order to follow a proper component structure, that will let clear what you want to render and how to properly split your data and responsibilities.
Looking to your JSON, we have a set of "skills" that have skills inside it (let's call them "innerSkills").
We can easily split it into 3 components, let's think together:
We can have a List that will render all your Skills.
We can have a Skill that will be responsible for rendering each Skill data, inside it, we will need to render the InnerSkills, so let's split it to another component.
We have then InnerSkill, that will be responsible for rendering each innerSkill that we have for each skill.
So, long story short, what we have is:
List -> Skill -> InnerSkills
Great, now that we established the split, let's see how we can make each component responsible for rendering its data.
Let's say we want to simply call <List skills={data} />. Following this, we can then start on the list itself, which would look something like:
const List = ({ skills }) => (
<ul>
{skills.map((skill, i) => (
<Skill key={i} skill={skill} />
))}
</ul>
);
Now that we are looping through all Skills and calling the Skill component for rendering it, we can take a look at how Skill should look, since it will also need to loop through skill.
const Skill = ({ skill }) => (
<li>
<p>Type: {skill.type}</p>
<ul>
{skill.skill.map((innerSkill, i) => (
<InnerSkill key={i} innerSkill={innerSkill} />
))}
</ul>
</li>
);
Great. Now we already have the two loops you need to render all the data, so it's just missing the definition on how each InnerSkill should look like, and we can take a simplified approach and say we just want to render the name, so it could be something like:
const InnerSkill = ({ innerSkill }) => (
<li>
<p>Name: {innerSkill.name}</p>
</li>
);
To summarize this implementation, I made a simple code sandbox so you can See it live! and play around with the components.
I hope this clarifies your question and helps you to think better in the future on how you want to organize stuff, first check how to split, later how to render. Don't try to start rendering everything inside loops because it will get nasty.

There are two things in your code causing this error:
var skills = this.props.data.skills.map((skills) =>{
var skill = skills.skill.map(function(skill){
// should be <li key={skill.name}>{skill.name}</li>
return <li key={skill.name}>{skill}</li>
})
// should be <ul key={skills.type}>{skill}</ul>
return <ul key={skills.type}>{skills}</ul>
})
Assuming you want a single unordered list of all skill names, I'd suggest using the flatMap() function to re-write this as follows:
<div className="bars">
<ul className="skills">
{this.props.data.skills.flatMap((skillGroup) =>
skillGroup.skill.map((skill) => (
<li key={skill.name}>{skill.name}</li>
))
)}
</ul>
</div>

Related

Failing to map property values in React.js and Typescript

I'm quite new to React and Typescript. I do have an object that has property values as arrays.I can get the object's value through the below logic.
let cityObj = {
"Harare":[
{
"released_on":"2007-11-08T00:00:00",
"slug":"0985"
},
{
"released_on":"2007-11-08T00:00:00",
"slug":"2346"
}
],
"bulawayo":[
{
"released_on":"2007-11-08T00:00:00",
"slug":"9383"
}
]
}
Object.keys(cityObj).forEach(el =>{
console.log(cityObj[el]);
})
The output is as follows;
array(3)
array(1)
This is what i'm seeking to do in them jsx react return function. By i'm only getting the city name e.g Harare and Bulawayo after using the code below.
{Objects.keys(cityObj).map((city:any) =>
<div> {city}
cityObj[city].map((el:any) => <span>- {el.slug}</span>)
</div>
)}
What I really want is something like this:
Harare
-0985
-2345
Bulawayo
-9383
But in this case, I'm only getting city names and the city data is not showing.
seems you just forget to pass inside map to a react executable code block
{Objects.keys(cityObj).map((city:any) =>
<div> {city}
{cityObj[city].map((el:any) => <span>- {el.slug}</span>)}
</div>
)}

Iterate through Object of arrays in react js

I am trying to render the following json response.
"tickets": {
"tickets": [
"A Nuisance in Kiribati",
"A Nuisance in Saint Lucia"
]
}
My attempt is as below.
const display4 = Object.keys(this.state.ticketsList).map(
(keyName, keyIndex) => {
return (
<div className="my-posts">
<li key={keyIndex}>{keyName}_{keyIndex}:</li>
<li key={keyIndex}>{this.state.ticketsList[keyName]}</li>
</div>
);
}
);
But it will display the tickets array in a single line without any separation even. I tried all most all the things on the internet. But nothing works yet. How can I fix this?
You are reading the raw array and displaying it - that's why it displays all in one line. this.state.ticketsList[keyName] is the array. So you need to iterate through it in an additional loop to display each item.
Try this:
const display4 = Object.values(this.state.ticketsList).map(
tickets => tickets.map((ticketName, index) =>
<div className="my-posts">
<li key={index}>{ticketName}_{index}:</li>
<li key={index}>{ticketName}</li>
</div>
)
);

React - Not able to update list of product component

I made a list of product which contains name, price etc.. properties. Then I created a simple search box and I am searching my products based on product name . the search result returns the correct object but the UI is not getting updated with that result.Initially, I am able to see the list but after searching it is not getting updated. I am new to react SO need some help. here is my code
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
console.log(result);
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
console.log(list);
this.setState({listOfProducts:list});
}
render()
{
this.state.listOfProducts=this.products.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
return <div>
<input onChange={event=>{this.OnInputChange(event.target.value)}}/>
<ul>{this.state.listOfProducts}</ul>
</div>
}
}`
this.products.filter should probably be this.state.products.filter
When referring to a Components method you say this.onInputChange but when referencing state you must say this.state.products because this.products doesn't exist.
I would also move this:
let list=result.map((product)=>
{
return <li key={product.price}>{product.name}</li>
});
to your render statement. so your onchange handler could be:
OnInputChange(term)
{
let result= this.products.filter(product=>{
return product.name==term;
});
this.setState({listOfProducts:result});
}
and then you
render(){
return(
{this.state.listOfProducts.map(product => {
return <li key={product.price}>{product.name}</li>
})}
)
}
hope that helps! If your problem persists please share your entire code so I can better understand the issue.

Reactjs - Need to bind a onClick handler twice?

Coming from Vue.js (two way data flow) I have question about react one way data flow - I have a Parent that have a handler for its child onClick:
<div className="recipe-container">
<button onClick={this.toggleRecipeList.bind(this)}>Show Recipes</button>
<RecipeList showRecipe={this.showRecipe} list={this.state.recipes} />
</div>
So, I pass showRecipe handler, which has only one parameter (and simply just logs it to the console).
My RecipeList looks like this (stateless func):
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={props.showRecipe(rec)} key={key}>{rec.title}</li>
})
}
</ul>)
I tried to launch showRecipe(rec) to have the current rec object as argument. Although I recive what I want, the handler is being fired from a button which is a sibling of RecipeList.
I manage to get it working by adding onClick={props.showRecipe.bind(null,rec)} to li element, but I find it really dirty way to do so.
Am I missing something? I thought showRecipe(rec) would be enough to get what I wanted. Why showRecipe(rec) is being fired with this set to button?
I think that your second snippet has a classic error:
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={props.showRecipe(rec)/*here*/} key={key}>{rec.title}</li>
})
}
</ul>)
You are assigning the result of calling showRecipe to the onClick parameter, not the function itself. The bind solutions works, but if you want to pass the parameter rec without using bind you need to wrap the call:
return (<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={()=>props.showRecipe(rec)} key={key}>{rec.title}</li>
})
}
</ul>)
You can use es2015 stage-0 syntax in order to write it like this:
class Blah extends Component {
onClick = (recipe) => e => this.props.showRecipe(recipe);
render() {
return (
<ul className='recipe-list-bottom'>
{
props.list.map((rec, key) => {
return <li onClick={onClick(rec)} key={key}>{rec.title}</li>
})
}
</ul>
)
}
}

can't find variable: x - looping through an array in ReactJS

I'm trying to iterate through an array stored in the store.
I get this red box error: can't find variable: city.
Is this a correct way to loop through an array in reactJS?
class CitiesPage extends Component {
render() {
return (
<View>
<Text>Cities Info:</Text>
<div>
{this.props.citiesArr}.map(function(city) {
<li key={city}>{city}</li>
});
</div>
</View>
);
}
}
My array inside the store looks like this:
const STORE_STATES = {
citiesArr: [
{city:'Rome', population:'34454'},
{city:'Paris', population:'45678'},
{city:'London', population:'2334'},
{city:'Milan', population:'23456'},
{city:'Amsterdam', population:'1234'},
{city:'Dublin', population: '234'},
{city:'Valencia', population: '2345'},
{city:'Ankara', population: '3456'}
]
};
{
this.props.citiesArr.map(function(city) {
<li key={city}>{city}</li>
})
}
// or ES6 arrow function
{this.props.citiesArr.map((city) => <li key={city}>{city}</li>)}
Instead of
{this.props.citiesArr}.map(function(city) {
<li key={city}>{city}</li>
});
In JSX (that code that looks like HTML but actually isn't), JavaScript has to be between { } and you misplaced the second one. Babel most likely transpiles your JSX into a code that does something totally different and causes the error. Check the transpiled source code if you're not 100% sure.
<div>
{
this.props.citiesArr.map((city) =>
<li key={city}>{city}</li>
);
}
</div>
Javascript is written inside jsx using { [your javascript code] }.

Resources