Cannot map subchildren of children - javascript

My file structure has a questions array, in which there's one more section_questions array I want to access.
The React code looks like this
<div className="questions-container">
{employee.questions.map(question => (
<p className="section-title">{question.section_name}</p>
/* This won't work, syntax error */
{ question.section_questions.map(sq => ( <p>Yo</p> )) }
)
)}
</div>
JSON-Object:
"questions": [
{
"section_name": "Random",
"section_questions": [
{
"question": "Q1",
"answer": "A1"
},
{
"question": "Q2",
"answer": "A2"
}
]
}
]
Why won't this work? How can I fix this?
The error is Parsing error: Unexpected token, expected ","

The problem seems to be that you need a tag (or fragment) that encloses everything underneath.
So you could:
move the < /p> to be after { question.section_questions.map(sq => ( <p>Yo</p> )) } .
Or enclose everything in a div.
Or use a react fragment < React.Fragment>< React.Fragment/> instead of div. Suggested by: #Antoan Elenkov
See: reactjs.org/docs/fragments.html
Example:
class TodoApp extends React.Component {
constructor(props) {
super(props)
this.questions = [
{ section_name: "Learn JavaScript", section_questions: [1] },
{ section_name: "Learn React", section_questions: [1] },
{ section_name: "Play around in JSFiddle", section_questions: [1] },
{ section_name: "Build something awesome", section_questions: [1] }
];
}
render() {
return (
<div>
<h2>Todos:</h2>
{this.questions.map(question => (
<p className="section-title">
{question.section_name}
{question.section_questions}
{question.section_questions.map(sq => ( <p>Yo</p> ))}
</p>
)
)}
</div>
)
}
}
ReactDOM.render(<TodoApp />, document.querySelector("#app"))
Fiddle: https://jsfiddle.net/opy9k5hr/
Update
Another option is to enclose everything in a <div> tag.
{this.questions.map(question => (
<div>
<p className="section-title"> {question.section_name} </p>
{question.section_questions}
{question.section_questions.map(sq => ( <p>Yo</p> ))}
</div>
)
)}

Related

setting state within nested response

I am building a react app and I am setting the state with api of nested response of nested state But the state is not setting the way I want.
response that is receiving from api
[
{
"id": 70,
"title": "feefifef",
"images": [
{
"id": 28,
"text": "First Image"
"blog_id": 70,
},
{
"id": 28,
"text": "First Image",
"blog_id": 70,
}
]
}
]
App.js
class App extends React.Component {
constructor(props){
super(props)
this.state = {
blogs = [
{
id: 0,
title: "",
images: [
{
id:0,
text:""
}
]
}
]
}
}
componentDidMount() {
let data;
axios.get('http://127.0.0.1:8000/api/blogs/').then((res) => {
data = res.data;
this.setState({
blogs: data.map((blog) => {
return Object.assign({}, blog, {
id: blog.id,
title: blog.title,
images: blog.images,
}
})
})
})
}
render() {
const blogs = this.state.blogs.map((blog) => (
<BlogList
id={blog.id}
title={blog.title}
images={blog.images}
/>
))
}
return (
<div>{blogs}</div>
)
}
class BlogList extends React.Component {
constructor(props){
super(props)
}
return (
<div>
Title: {this.props.title}
Images: {this.props.images}
</div>
)
}
What is the problem ?
Images are not showing after Title. I am trying to show all images in BlogList class of every blog.
I have also tried using (in BlogList class)
this.props.images.map((img) => {
return (
<div>
Title: {this.props.title}
Images: {img.text}
</div>
)
}
But it showed me
this.props.images.map is not a function.
then I think the problem is with setting state of images (I may be wrong).
When I tried to print this.props.images then it is showing
0: {id: 28, text: '1111', blog_id: 71}
length: 1
[[Prototype]]: Array(0)
I am new in react, Any help would be much Appreciated. Thank You in Advance
this.props.images is an array and hence you can't use {this.props.images} directly. Other wise you will get an error like this "Objects are not valid as a React child. If you meant to render a collection of children, use an array instead"
You have to use something like this
render() {
return (
<div>
Title: {this.props.title} <br/>
Images:
{this.props.images?.map((image, i) => (
<div key={image.id}>
{image.id}<br/>
{image.text}<br/>
{image.blog_id} <br/>
</div>
))}
</div>
);
}

Avoid unnecessary component rendering with memo in nextjs

I'am trying to understand react's behaviour throught nextjs.
I have an index.js page with one component Homecard displayed three times and one button that increment a value.
Each time I click on button all Homecard components are re-render.
index.js
import { Homecard } from '../components/Homecard'
import { useState } from 'react'
export default function Home() {
const [count, increment] = useState(0);
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
const handleCount = () => {
increment(count => count + 1);
}
return (
<>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</>
)
}
homecard.js
export default function Homecard({ main, sub, desc, nav }) {
console.log('render Homecard');
return (
<div className={`${styles.homecard}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<Link href={link.href}>
<a>{link.value}</a>
</Link>
</li>
))}
</ul>
}
</div>
</div>
)
}
I tried to wrap my Homecard with React.memo like so
const Homecard = React.memo(({ main, sub, desc, nav }) => {})
But I still see console.log('render Homecard'); when my button is clicked.
How can I could update only my button and not others components ?
The problem is that you're recreating your homecards array on every render of Home, so each nav object is a new object, and so React.memo sees a difference in the props and doesn't optimize away the subsequent re-renders.
There are a couple of ways to fix it:
Define homecards outside Home; it's unchanging, so there's no reason to recreate it every time Home is called.
If you couldn't do that for some reason, you could pass a second argument to React.memo, the "areEqual" function, which will receive the old props and the new ones; in the function, do a deep comparison to see if if nav objects have the same property values are the previous ones even though they're different objects.
Example of #1:
const { useState } = React;
/*export default*/ const Homecard = React.memo(({ img, main, sub, desc, nav }) => {
console.log('render Homecard');
return (
<div className={`${""/*styles.homecard*/}`}>
<div>
<h3>
{main}
{sub &&
<span>{sub}</span>
}
</h3>
<p>{desc}</p>
{nav &&
<ul>
{nav.map((link, index) => (
<li key={index}>
<a href={link.href}>
{link.value}
</a>
</li>
))}
</ul>
}
</div>
</div>
)
});
const homecards = [
{
"main": "main0",
"sub": "sub0",
"desc": "Desc0",
"nav": [{
"href": "/page0",
"value": "see"
}]
},
{
"main": "main1",
"sub": "sub1",
"desc": "Desc1",
"nav": [{
"href": "/page1",
"value": "see"
}]
},
{
"main": "main2",
"sub": "sub2",
"desc": "Desc2",
"nav": [{
"href": "/page2",
"value": "see"
}]
}
];
/*export default*/ function Home() {
const [count, increment] = useState(0);
const handleCount = () => {
increment(count => count + 1);
}
return (
// Sadly, Stack Snippets don't support <>...</>
<React.Fragment>
<div className='d-flex justify-content-between' style={{ marginLeft: '-1.5rem' }}>
{
homecards.map((homecard, index) => (
<Homecard
key={index}
main={homecard.main}
sub={homecard.sub}
desc={homecard.desc}
nav={homecard.nav}
/>
))
}
</div>
<button onClick={handleCount}>increment {count}</button>
</React.Fragment>
)
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<Home />);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
(Warning: I changed a couple of minor things to make this work in Stack Snippets, like replacing Link and using React.Fragment instead of <>...</>, so don't copy and paste it directly back into your project, just move homecards in your existing code.)

map function not showing elements on screen

i have this part of code the map function did not show any element of the array, if i console.log the variable it shows me the elements but for some reasons i can't show the elements on the screen.
Code
function Solution({list}){
const data = list
console.log(data);
return(
<div>
{
data?.map((item) => {
return (
<div>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export default Solution;
const list = [
{
title: "Home"
},
{
title: "Service",
subItem: ["Clean", "Cook"]
},
{
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
Solution({list})
Please, just pass "list" link this.
<Solution list={list}/>
Hope will help you, Thanks)
Check this out
import React from 'react';
function Solution({list}){
const data = list
console.log(list);
return(
<div>
{
data?.map((item) => {
return (
<div key={item.id}>
<p> {item.title} </p>
</div>
)
})
}
</div>
)
}
export function App(props) {
const list = [
{
id:1,
title: "Home"
},
{
id:2,
title: "Service",
subItem: ["Clean", "Cook"]
},
{
id:3,
title: "Kitchen",
subItem: ["Wash", "Dish"]
},
];
return (
<div className='App'>
<Solution list={list} />
</div>
);
}
// Log to console
console.log('Hello console')
Have a unique key prop for each element when you map an array and send list array as props to your Solution component

How can I loop over a JSON object in React

I'm trying to retrieve information from the following JSON object data.json:
{
"status": "ok",
"feed": {
"title": "NOS Nieuws",
},
"items": [
{
"title": "Test Title",
"description": "Test description",
"enclosure": {
"link": "https://examplelink.com/1008x567.jpg",
"type": "image/jpeg"
},
"categories": []
},
{
"title": "Test Title 2",
"description": "Test 2",
"enclosure": {
"link": "link": "https://examplelink.com/1008x567.jpg",
"type": "image/jpeg"
},
"categories": []
}
]
}
So I want to loop over this JSON object to display all the available items and its corresponding title, description and enclosure-link.
I know i can access them separately as:
const items = data.items;
const title = items.title;
const url = items.enclosure.link;
Usually I would do a for-loop and loop through data.items[i]. However, since this is a react and an object instead of an array it works differently.
My current code:
class Feed extends Component {
render() {
const items = data.items[0];
const title = items.title;
const url = items.enclosure.link;
const description = items.description;
const feed = [
{
url: url,
title: title,
description: description
}
];
return (
<div className="feed">
<h1>Newsfeed</h1>
<div className="columns is-multiline">
{feed.map(article => (
<div className="column is-one-third">
<NewsCard
article={article}
title={items.title}
description={items.description}
/>
</div>
))}
</div>
</div>
);
}
}
Right now its only displaying the first entry of the object (because it has const items = data.items[0]) How can I loop over data.json and display its content in the NewsCard component? I know that each child should have a unique 'key' prop but thats where I'm stuck.
I want to loop over this JSON object to display all the available
items and its corresponding title, description and enclosure-link
Then instead of doing this:
const items = data.items[0];
Try this:
const items = data.items;
Then, you can use the map function, like this:
items.map(item => (
<div className="column is-one-third">
<NewsCard
article={item.enclosure.link}
title={item.title}
description={item.description}
/>
</div>
));
You could do something like this.
class Feed extends Component {
render() {
let newsCards = data.items.map(item => {
<div className="column is-one-third">
<NewsCard
article={item}
title={item.title}
description={item.description}
/>
</div>
});
return (
<div className="feed">
<h1>Newsfeed</h1>
<div className="columns is-multiline">
{newsCards}
</div>
</div>
);
}
}
const feed = data.items
{feed.map(item => (
<div className="column is-one-third">
<NewsCard
article={item.enclosure.link}
title={item.title}
description={item.description}
/>
</div>
))}
try this way

Using map() to iterate through a nested Prop in React

I'm currently using react to render a prop called area which looks like this:
[{
"id": 1,
"name": "Europe",
"Countries": [{
"id": 1,
"name": "Iceland",
"Cities": [{
"id": 1,
"name": "Selfoss"
}]
}, {
"id": 2,
"name": "Switzerland",
"Cities": [{
"id": 2,
"name": "Geneva"
}]
}]
}, {
"id": 2,
"name": "Asia",
"Countries": [{
"id": 3,
"name": "Japan",
"cities": [{
"id": 3,
"name": "Yokohama"
}]
}]
}]
UPDATE 2--
This WORKS:
class AreaBar extends Component {
constructor(props) {
super(props);
}
.....
renderCountries() {
return(
<div>
This is the country
</div>
)
}
renderContinents() {
return(
<div>
This is the continent
{this.renderCountries()}
</div>
)
}
render() {
return(
<div>
{this.renderContinents()}
</div>
);
}
}
This outputs:
This is the continent
This is the country
Incorporating a map, this WORKS
renderContinents(area) {
return(
<div>
{area.name}
</div>
)
}
render() {
return(
<div>
{this.props.areas.map(this.renderContinents)}
</div>
);
}
}
This outputs:
Europe
Asia
BUT when I include {this.renderCountries()}, it doesn't output anything, which I think is why I couldn't get the suggestions to work.
renderCountries() {
return(
<div>
This is the country
</div>
)
}
renderContinents(area) {
return(
<div>
{area.name}
{this.renderCountries()}
</div>
)
}
render() {
return(
<div>
{this.props.areas.map(this.renderContinents)}
</div>
);
}
}
On Firefox, both of the continents show up but "this is a country doesn't show up" instead I get a
unreachable code after return statement
When an expression exists after a valid return statement,
a warning is given to indicate that the code after the return
statement is unreachable, meaning it can never be run.
It seems like it's saying renderCountries can never be run. I'm still a bit confused about this but I think I'm going to try to separate the components and see if it fixes the issue.
Two things:
1) In the second block of code in your question, you're doing area.countries.map. The key on your area object is called Countries, not countries. area.countries should be undefined.
2) area.Countries is an array of objects, like you said in your question. So yes, you can map over them just fine. The problem is that each Country is an object, and thus, you're trying to render an object as a child of your <div> in your renderCountries function. If you only want to display the Country's name, you should do something like this:
renderCountries(country){
return(
<div>
{country.name}
</div>
)
}
Then you will see some meaningful output.
you have a typo, use area.Countries instead of area.countries
Also I think you should create 3 components at least: Area, Country and City. Then you can render the data like so (please note I use ES6 syntax) :
var areas = [/* array you posted above*/];
// in your top-level component
render() {
return (<div>
{areas.map((area) => {
return <Area data={area}/>;
})}
</div>);
}
// Area component
export function Area({data}) {
render() {
return (<div>
Area name: {data.name}
{data.Countries.map((country) => {
return <Country data={country}/>
})}
</div>);
}
}
// Country component
export function Country({data}) {
render() {
return (<div>
Country: {data.name}
{data.Cities.map((city) => {
return <City data={city}/>
})}
</div>);
}
}
// City component
export function City({data}) {
render() {
return (<div>
City: {data.name}
</div>);
}
}

Categories

Resources