This question already has an answer here:
forEach() in React JSX does not output any HTML
(1 answer)
Closed 3 years ago.
The issue is that the variable is having the value when I console.log but it is not rendering in the page. The loop print only the first data and other data is not printed.
const langData = [
{
name: "Front-End",
children: [
{
name: "HTML"
},
{
name: "CSS"
},
{
name: "JS"
}
]
}
];
const renderName = ({ name, children }) => {
console.log(name);
return (
<>
<p>{name}</p>
{children && children.forEach(newData => renderName(newData))}
</>
);
};
const App = () => {
return <div className="App">{renderName(langData[0])}</div>;
};
Eg: Front-End will be on the page. Other data such as HTML, CSS, JS not showing in the page. But these variables are in the console.log. Not sure I miss to return any value.
Codesandbox link: https://codesandbox.io/s/reverent-ardinghelli-6snby?fontsize=14
Using map you can get go over the array. The reason why map works and forEach does not is because map returns a new array while forEach does not return anything(returns undefined). You also need to add keys to get rid of the warning. I've used the index of the array as the key here:
const renderName = ({ name, children }, key) => {
console.log(name);
return (
<>
<div key={key}>
<p>{name}</p>
{children && children.map((newData,index) => renderName(newData, index))}
</div>
</>
);
};
with render an array, you should use map, because .forEach always return undefined:
const renderName = ({ name, children, index }) => {
console.log(name);
return (
<div key={index}>
<p>{name}</p>
{children && children.map((item, index)=> {
const { name } = item;
return renderName({name, index})
})}
{/* {children && children.forEach(newData => renderName(newData))} */}
</div>
);
};
Try this you will directly get your result.
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const langData = [
{
name: "Front-End",
children: [
{
name: "HTML"
},
{
name: "CSS"
},
{
name: "JS"
}
]
}
];
const renderName = (name,data) => (
//console.log(name);
<>
<p>{name}</p>
{data && data.map(child => (
<p>{child.name}</p>
))}
</>
);
const App = () => {
return <div className="App">{renderName(langData[0].name,langData[0].children)}</div>;
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Related
how do I map over the following array of arrays and show each data contained in the objects separately? i.e I want the objects on the first array to be shown when a row on a table is expanded, the ones in the second array should be shown when another row is expanded and so on.
try to map like this:
array.map(subarray =>
subarray.map(item => ({ name: item.name, quantity: item.quantity })));
Why not loop over them and depict it as collapses?
import { useState } from "react";
const Collapse = ({ data, index }) => {
const [isOpen, setOpen] = useState(false);
const children = data.map(({ name, quantity }, key) => {
return (
<div key={key}>
<span>{`${name}:`}</span>
<span>{quantity}</span>
</div>
);
});
return (
<div>
<button onClick={() => setOpen(!isOpen)}>{`List ${index}`}</button>
<div>{isOpen && children}</div>
</div>
);
};
export default function App() {
const data = [
[
{ name: "Tusker", quantity: 9 },
{ name: "Guiness", quantity: 9 }
],
[
{ name: "Choma", quantity: 9 },
{ name: "Sprite", quantity: 9 }
]
];
return (
<div>
{data.map((localList, index) => {
return <Collapse key={index} data={localList} index={index} />;
})}
</div>
);
}
You might also check, if the collapse should be displayed at all - there might not always be data available.
This example isn't styled at all but it should lead you into the right direction.
I have some CMS content being returned and my goal is to have a year slider controlling the content depending on the year that the user selects by clicking the minus/plus arrow.
This is my code:
import "./styles.css";
import React from "react";
export default function App() {
return (
<div className="App">
<DatesProvider>
{data.map((item, index) => {
const Slice = slices[item.type];
return <Slice section={item.section} key={index} />;
})}
</DatesProvider>
</div>
);
}
const DateContext = React.createContext({});
const DatesProvider = ({ children }) => {
const [dates, setDates] = React.useState({});
return (
<DateContext.Provider value={{ dates, setDates }}>
{children}
</DateContext.Provider>
);
};
const DatePicker = ({ section }) => {
const { dates, setDates } = React.useContext(DateContext);
React.useEffect(() => {
// Set initial date
setDates((prevDates) => {
prevDates[section] = 2021;
return { ...prevDates };
});
// Clean up on dismount
return () => {
setDates((prevDates) => {
delete prevDates[section];
return { ...prevDates };
});
};
}, []);
const handlePlus = () => {
setDates((prevDates) => ({
...prevDates,
[section]: prevDates[section] + 1
}));
};
const handleMinus = () => {
setDates((prevDates) => ({
...prevDates,
[section]: prevDates[section] - 1
}));
};
return (
<div style={{ marginTop: 30 }}>
<button onClick={handleMinus}>-</button>
<span>{dates[section]}</span>
<button onClick={handlePlus}>+</button>
</div>
);
};
const Item = ({ section }) => {
const { dates } = React.useContext(DateContext);
return (
<div>
Section: {section} | Year: {dates[section]}
</div>
);
};
const data = [
{ type: "DatePicker", section: "foo" },
{ type: "Item", section: "foo" },
{ type: "Item", section: "foo" },
{ type: "DatePicker", section: "bar" },
{ type: "Item", section: "bar" },
{ type: "Item", section: "bar" }
];
const slices = { DatePicker, Item };
The result is currently this:
As you can tell it's returning the year slider several times and the structure is similar to this:
<slider> - 2021 + </slider>
<section class= "container-of-all-items">
<all-items></all-items>
</section>
<slider> - 2021 + </slider>
<section class= "container-of-all-items">
<all-items></all-items>
</section>
My goal is to have only one year slider wrapping/controlling the whole content items rather than the above repetition of sliders:
<slider> - 2021 + </slider>
<section class= "container-of-all-items">
<all-items></all-items>
</section>
Any idea how to achieve it by maintaining a map through the Slices?
I see, took me a while to understand, you basically want to have one set of + and - but list of items.
Then in your case, you code actually simplifies.
function Lists() {
const { dates, setDates } = React.useContext(DateContext);
const onClick = () => { setDates(...) }
return (
<>
<div onClick={onClick}>+</div>
<>
{dates.map((item, index) => {
return <Slice section={item.section} key={index} />
})}
</>
<div>-</div>
</div>
);
}
Then change your App.
export default function App() {
return (
<div className="App">
<DatesProvider value={...}>
<Lists />
</DatesProvider>
</div>
);
}
Actually you might not need the context at all, since the logic has been promoted to the parent. But it's up to you.
from an array inside an object and renders it to the browser. The App uses a number of small components Header, Contents and Total to function.
Next part of exercise is to create a single component 'Course' to do the same job as all the smaller components(Header,Contents,Total)
Here is original app that gives this output:
Half Stack application development
Fundamentals of React10
Number of exercises is 31
import React from 'react'
const Header = (props) => {
return (
<div>
<h1>{props.course}</h1>
</div>
)
}
const Content = (props) => {
return (
<div>
<p>{props.part1}</p>
</div>
)
}
const Part = (props) => {
return (
<div>
<p>{props.part1} {props.exercises1}</p>
</div>
)
}
const Total = (props) => {
return (
<div>
<p>Number of exercises is {props.total}</p>
</div>
)
}
const App = () => {
const course = {
name: 'Half Stack application development',
parts: [
{
name: 'Fundamentals of React',
exercises: 10
},
{
name: 'Using props to pass data',
exercises: 7
},
{
name: 'State of a component',
exercises: 14
}
]
}
return (
<div>
<Header course={course.name} />
<Content part1 = {course.parts[0].name + course.parts[0].exercises} />
<Total total = {course.parts[0].exercises + course.parts[1].exercises + course.parts[2].exercises} />
</div>
)
}
export default App
And here is the new component I created which is giving me a blank output, no errors reported
const Course = (props) => {
return (
<div>
<Header></Header>
<Content></Content>
<Total></Total>
</div>
)
}
With updated App
onst App = () => {
const course = {
name: 'Half Stack application development',
parts: [
{
name: 'Fundamentals of React',
exercises: 10
},
{
name: 'Using props to pass data',
exercises: 7
},
{
name: 'State of a component',
exercises: 14
}
]
}
return <Course course={course} />
}
export default App
I'm following a React course and I'm trying to do some experiments around the code to have a better understanding of concepts.
I have some dummy data:
export const data = [
{ id: 1, name: 'john' },
{ id: 2, name: 'peter' },
{ id: 3, name: 'susan' },
{ id: 4, name: 'anna' },
];
and this is my component:
import React from "react";
import { data } from "../../../data";
const UseStateArray = () => {
const [people, setPeople] = React.useState(data);
return (
<>
{people.map((person) => {
const { id, name } = person;
return (
<div key={id} className="item">
<h4>{name}</h4>
</div>
);
})}
<button
type="button"
className="btn"
onClick={() => setPeople([])}
>
Clear Items
</button>
</>
);
};
export default UseStateArray;
The button has an event handler on click which calls setPeople with an empty array (so to remove all of the elements).
I was trying to change the funcionality of such button, trying to change the name of the first element of my array of objects (data) in the following way:
onClick={() => setPeople(people[0].name = 'Frank')}
Doing this, get an error, namely: "TypeError: people.map is not a function".
I think the reason is because I'm not returning an array anymore and therefore map fails to run.
How can I simply change the name (or any value) of an object present in an array?
You are mutating the object
clickHandler = () => {
const newPeople = people.map((person, index) => {
if (index === 0) {
return {
...person,
name: 'Frank'
}
}
return person;
});
setPeople(newPeople);
}
....
....
onClick={clickHandler}
You need to copy the array into a newer version.
Extract the object out of the array using the index property.
Update the field.
function App() {
const [data, setData] = React.useState([
{ name: "Hello", id: 1 },
{ name: "World", id: 2 }
]);
function changeName(idx) {
const newData = [...data];
newData[idx].name = "StackOverFlow";
setData(newData);
}
return (
<div>
{data.map((d, idx) => {
return (
<div>
<p>{d.name}</p>
<button
onClick={() => {
changeName(idx);
}}
/>
</div>
);
})}
</div>
);
}
NOTE :-
Mutation is not allowed on the state, which basically means you cannot change the state directly. Copy the object or an array and do the updates.
I have a react js code in a magento pwa app.
It has a component called categoryList and I need to add a horizontal scroll menu for that category list.
Following is my code
const mapCategory = categoryItem => {
const { items } = categoryItem.productImagePreview;
return {
...categoryItem,
productImagePreview: {
items: items.map(item => {
const { small_image } = item;
return {
...item,
small_image:
typeof small_image === 'object'
? small_image.url
: small_image
};
})
}
};
};
const list = [
{ name: 'item1' },
{ name: 'item2' },
{ name: 'item3' },
{ name: 'item4' },
{ name: 'item5' },
{ name: 'item6' },
{ name: 'item7' },
{ name: 'item8' },
{ name: 'item9' }
];
const MenuItem = ({ text, selected }) => {
return (
<div
className="menu-item"
>
{text}
</div>
);
};
export const Menu = (list) => list.map(el => {
const { name } = el;
return (
<MenuItem
text={name}
key={name}
/>
);
});
const Arrow = ({ text, className }) => {
return (
<div
className={className}
>{text}</div>
);
};
const ArrowLeft = Arrow({ text: '<', className: 'arrow-prev' });
const ArrowRight = Arrow({ text: '>', className: 'arrow-next' });
const CategoryList = props => {
const { id, title } = props;
const talonProps = useCategoryList({
query: categoryListQuery,
id
});
const { childCategories, error, loading } = talonProps;
const classes = mergeClasses(defaultClasses, props.classes);
console.log('ssss' +childCategories);
const header = title ? (
<div className={classes.header}>
<h2 className={classes.title}>
<span>{title}</span>
</h2>
</div>
) : null;
let child;
if (error) {
child = (
<div className={classes.fetchError}>
Data Fetch Error: <pre>{error.message}</pre>
</div>
);
}
if (loading || !childCategories) {
child = fullPageLoadingIndicator;
} else if (childCategories.length === 0) {
child = (
<div className={classes.noResults}>No child categories found.</div>
);
} else {
const { selected } = this.state;
// Create menu from items
const menu = Menu(list, selected);
child = (
<div className={classes.content}>
{childCategories.map((item, index ) => (
<CategoryTile item={mapCategory(item)} key={index} />
))}
<ScrollMenu data={menu}
arrowLeft={ArrowLeft}
arrowRight={ArrowRight}
onSelect=''
/>
</div>
);
}
return (
<div className={classes.root}>
{header}
{child}
</div>
);
};
CategoryList.propTypes = {
id: number,
title: string,
classes: shape({
root: string,
header: string,
content: string
})
};
export default CategoryList;
I get the following error when I try to use this code. The error seems to be about not being to resolve a specific package or module.
ERROR in ./src/components/CategoryList/categoryList.js
Module not found: Error: Can't resolve 'react-horizontal-scrolling-menu/build/scrollMenu' in '/var/www/html/apekade/apekade-pwa/packages/pwa-neosolax/src/components/CategoryList'
ℹ 「wdm」: Failed to compile.
I dont know if I have placed the code correct.I'm a beginner.Please help
Running a simple "npm install --update --save" worked for me, after struggling for an hour to resolve this issue.
This usually means that the particular package/dependency (in this case "react-horizontal-scrolling-menu") is not installed
you can install it by using "npm install react-horizontal-scrolling-menu" or "yarn add react-horizontal-scrolling-menu"
If you are working on a project then you can go to 'package.json' and add "react-horizontal-scrolling-menu": "^2.7.1" or any other version u need and then go to the terminal and type "npm install --legacy-peer-deps"