I am trying to use a forEach loop to return a component for each item in an array. However, when my react app loads it just returns [Object] over and over again. Why is this and how do I fix it?
Here is the code:
const ProductList = () => {
let product_image;
let product_heading;
let product_listbox_options = "";
info.data.products.edges.forEach((edge) => {
console.log(edge);
const product = edge.node;
product_heading = edge.node.title;
if (
product.media !== undefined &&
product.media.edges !== undefined &&
product.media.length > 0
) {
product.media.edges.forEach((media) => {
if (media.node.mediaContentType === "IMAGE") {
product_image = (
<Thumbnail
source={media.node.image.originalSrc}
alt={product.title}
/>
);
}
});
}
product_listbox_options += (
<Listbox.Option>
<Heading>{product_heading}</Heading>
{product_image}
</Listbox.Option>
);
});
return product_listbox_options;
};
What you are doing here
product_listbox_options += (
<Listbox.Option>
<Heading>{product_heading}</Heading>
{product_image}
</Listbox.Option>
);
is you are adding an empty string value to a react component which results in [Object].
As #tieulinh said you should use .map() instead of .forEach() if you want to return a list/array of components which can be rendered by react.
So your component becomes like this:
const ProductList = () => {
let product_image;
let product_heading;
return (
<>
{info.data.products.edges.map((edge, index) => {
const product = edge.node;
product_heading = edge.node.title;
if (
product.media !== undefined &&
product.media.edges !== undefined &&
product.media.length > 0
) {
product.media.edges.forEach((media) => {
if (media.node.mediaContentType === "IMAGE") {
product_image = (
<Thumbnail
source={media.node.image.originalSrc}
alt={product.title}
/>
);
}
});
}
return (
//Change this key to something meaningful from your edge object
<Listbox.Option key={index}>
<Heading>{product_heading}</Heading>
{product_image}
</Listbox.Option>
);
})}
</>
);
};
Try doing it this way:
console.log(JSON.stringify(edge, null, 2))
ReactJS doesn't work like this
You can use the map method instead of forEach
Related
I have a list of records that fills a table and adds a remove button next to each record. When I press the remove button, in the console I can see that the records really did decrease by 1 and the item I selected to be removed is actually removed, but that change won't show on the page in the table itself. What could be the problem?
As you can see in the image below, when I click the remove button, the number or records decreases but the item wont be removed from the table itself.
const AddingTable = () => {
const [records, setRecords] = useState(someRecordsReturnedFromAnAPI);
let filterFn = { fn: items => { return items; } };
let headCells = [...];
const { TblContainer, TblHead, TblPagination, recordsAfterPagingAndSorting } = useTable(records, headCells, filterFn);
function handleRemoveClick(itemList, item) {
const index = itemList.indexOf(item);
itemList.splice(index, 1);
setRecords(itemList);
}
function TableRowWithRemove(props) {
let { itemList, item } = props;
return (
<TableRow>
//... Table cells with records contents
<TableCell>
<button onClick={() => handleRemoveClick(itemList, item)}> Remove </button>
</TableCell>
</TableRow>
)
}
return (
<>
<TblContainer>
<TblHead />
<TableBody>
{
recordsAfterPagingAndSorting().map((item, index) => (
<ConditionalTableRow key={index} item={item} itemList={records} />
))}
</TableBody>
</TblContainer>
<TblPagination />
<ConditionalDiv />
</>
);
}
const TblPagination = () => (
typeof records === 'undefined'?
[]
:
<TablePagination
component="div"
page={page}
rowsPerPageOptions={pages}
rowsPerPage={rowsPerPage}
count={records.length}
onPageChange={handleChangePage}
onRowsPerPageChange={handleChangeRowsPerPage}/>
)
const recordsAfterPagingAndSorting = () => {
if (typeof records !== 'undefined') {
return stableSort(filterFn.fn(records), getComparator(order, orderBy))
.slice(page * rowsPerPage, (page + 1) * rowsPerPage)
}
else
return null;
}
if (typeof recordsAfterPagingAndSorting !== 'undefined')
return {
TblContainer,
TblHead,
TblPagination,
recordsAfterPagingAndSorting
}
else
return {
TblContainer,
TblHead,
TblPagination
}
}
try using, since splice doesn't change the address of object.
function handleRemoveClick(itemList, item) {
const index = itemList.indexOf(item);
let newItemList =[...itemList];
newItemList.splice(index, 1);
setRecords(newItemList);
}
I am using React map and I am getting the following error
TypeError: Cannot read property 'PreviewThemeSideBar' of undefined
I don't understand in any way why I get undefined because I checked the imported component where I pass props and there I get all the data, see
SeleceColorsTheme.js
export default function SelectColorsTheme(props) {
const groupSize = 3;
const [selectedIndex, setSelectedIndex] = useState(false);
const setBorder = (index) => {
setSelectedIndex(index);
};
const rows = SideBarColors.map(function (col, index) {
const selectBorder = classNames({
'builtin_theme_preview': true,
'selectBorder': index === selectedIndex ? 'selectBorder' : null
});
// map content to html elements
return <SelectThemeContent {...props} selectBorder={selectBorder}
col={col} setBorder={setBorder} index={index}/>
}).reduce(function (r, element, index) {
// create element groups with size 3, result looks like:
// [[elem1, elem2, elem3], [elem4, elem5, elem6], ...]
index % groupSize === 0 && r.push([]);
r[r.length - 1].push(element);
return r;
}, []).map(function (rowContent) {
// surround every group with 'row'
return <div className="SelectThemePictures_Separator">{rowContent}</div>;
});
return <div className="container">{rows}</div>;
};
SelectThemeContent.js
export default function SelectThemeContent(props) {
const selectBorder = props.selectBorder;
const col = props.col;
const setBorder = props.setBorder;
const index = props.index;
return(
<div className={selectBorder} key={index} onClick={() => props.SideBarPageContent(col) || setBorder(index)}>
<div style={{ background: col.PreviewThemeSideBar }} className="builtin_theme_preview__nav">
<div className="builtin_theme_preview__search" />
...
</div>
</div>
);
}
I just added a check condition to each props, example
<div style={{ background: col && col.PreviewThemeSideBar }}</div>
I'm new in React. I'm developing a screen but I have a issue, I don't know how insert the children in the parent if the state condition is equals, I'm using an array to print the parent and children but depends of the data the parent could have a children or not, for example if (parent.rework_name === children.rework_name) ? print the children : 'nothing in the parent'.
Please let me know if you have an idea how to solve this, many many thanks in advance.
This is the goal, my code works but the damn children is outside the parent :(
class Filling extends Component {
constructor() {
super();
this.state = {
fillingStations: [],
};
}
componentDidMount() {
getDataAPI('http://localhost:8080/api/parent')
.then((station) => {
getDataAPI('http://localhost:8080/api/children')
.then((data) => {
const stationArray = [];
station.map((item, index) => {
stationArray.push(
<ReworkStation key={index} title={index + 1} status='' />,
);
data.map((it, idx) => {
const f2Date = it.f2_time.substr(0, 10);
const f2Hour = it.f2_time.substr(11, 8);
const f2DateFormatted = `${f2Date.substr(8, 2)}/${f2Date.substr(5, 2)}/${f2Date.substr(0, 4)}`;
const color = selection_color(it.color_d);
return (
stationArray.push(item.rework_name === it.rework_name && <ReworkTitle key={idx} vin={it.vin} date={f2DateFormatted} ipsq={it.defects} hour={f2Hour} color={color} />)
);
});
});
console.log(stationArray);
this.setState({
fillingStations: stationArray,
});
});
});
}
render() {
return (
<div className='row'>
{ this.state.fillingStations }
</div>
);
}
}
I don't know how to insert the children inside the parent already render.
I already solved, first render all the parent divs and after replace the position array with array.splice
render() {
const array = [];
this.state.fillingStations.map((item, index) => (
array.push(<Parent key={index} title={index + 1} status='' />),
this.state.fillingChildren.map((it, ind) => {
if (item.name === it.name) {
parent.splice(index, 1,
<Parent {...this.props}}>
<Child {...this.props} />
</Parent >);
}
})
));
return (
<div className='row'>
{array}
</div>
);
}
}
Hi i have problem with my react app. I have page with list of Subjects which data i get with react-apollo query. This page has action which links me to another component.
And then in that Child component i have Button Back which when i click it send me back to that lists of views... BUT this time it throws me null pointer error and i don't why it is happened.
const getSubject = `query GetSubject($id: ID!) {
getSubject(id: $id) {
id
name
description
packages(limit:999) {
items {
id
name
description
order
themes(limit:999){
items {
id
name
order
}
}
}
}
}
}
`;
function SubjectView(props) {
const classes = useStyles();
let width = window.innerWidth;
let years = [];
const [rocnikValue, setRocnik] = useState(0);
const [mobile, setMobile] = useState(0);
useEffect(() => {
function changeSize() {
if ((window.innerWidth < 960) && (mobile === false)) {
setMobile(true);
}
else if ((window.innerWidth > 960) && (mobile === true)) {
setMobile(false);
}
else return;
}
window.addEventListener("resize", changeSize.bind(this));
return function cleanup() {
window.removeEventListener("resize", changeSize.bind(this));
};
});
const handleSelect = event => {
setRocnik(event.target.value);
};
return (
<>
<Query
query={gql(getSubject)}
variables={{ id: props.match.params.subjectId }}
>
{result => {
if (result.loading) {
return (
<LinearProgress />
);
}
if (result.error) {
return (
<Typography color="error" variant="body1">
{result.error}
</Typography>
);
}
/* HERE I GET NULL POINTER ERROR */
result.data.getSubject.packages.items
.sort((a, b) => a.order - b.order)
.map((item,i) => years[i] = item.name)
if (!rocnikValue.length) {
setRocnik(years[0]);
return null;
}
if (width < 960) {
if (!mobile.length) setMobile(true);
return (
<div className={classes.page}>
<SubjectHeader
subject = {result.data.getSubject}
years = {years}
handleSelect = {handleSelect}
rocnik = {rocnikValue}
/>
{result.data.getSubject.packages.items
.sort((a, b) => a.order - b.order)
.map((pkg, pkgIndex) => (
<Fragment key={pkgIndex}>
{pkg.name === rocnikValue &&
<MobileView
key = {pkgIndex}
rocnik = {pkg}
/>
}
</Fragment>
))}
</div>
);
}
else {
if (!mobile.length) setMobile(false);
return (
<div className={classes.page}>
<SubjectHeader
subject = {result.data.getSubject}
years = {years}
handleSelect = {handleSelect}
rocnik = {rocnikValue}
/>
<DesktopView
subject = {result.data.getSubject}
rocnik = {rocnikValue}
/>
</div>
);
}
}}
</Query>
</>
);
}
Child component with back button is not important i think.
Anyway why is this happening ?
You have checked variables loading and error. I would also check the data before using that. Something like this:
if (result.data && result.data.hasOwnProperties('getSubject') && result.data.getSubject) {
...insert your actions here
} else {
return null <== If it's Ok...
}
I'm getting an error that i'm missing key prop for my map iteration.Got confused where i'm missing one . I have a map inside map.Could you please help me
displayData() {
const { data, index } = this.state;
let sortedData = data[index].settings.map((item, id) => {
const { _init_ } = item.settings;
return _init_.map((message, index) => {
const { message_content } = message;
return message_content === undefined ? null : (
<>
<div>
<div key={index} className="settings-message">
{message_content}
</div>
</div>
<div>yes</div>
</>
);
});
});
return sortedData;
}
The key should be on the parent div.
return message_content === undefined ? null : (
<div key={index}>
<div className="settings-message">
{message_content}
</div>
</div>
)
Your top level component needs a unique key. Use explicit fragment syntax and add the key to the fragment,
displayData() {
const { data, index } = this.state;
let sortedData = data[index].settings.map((item, id) => {
const { _init_ } = item.settings;
return _init_.map((message, index) => {
const { message_content } = message;
return message_content === undefined ? null : (
<React.Fragment key={index}>
<div>
<div className="settings-message">{message_content}</div>
</div>
<div>yes</div>
</React.Fragment>
);
});
});
return sortedData;
}
ps. You may have one more div than you actually need