problem with infinite loop in function hook useEffect - javascript

I have a problem with an infinite loop on my hook,
I'm passing the data of the local JSON breakfast. If you are iterating with map the data and I am taking it to paint a menu of buttons.
If I remove the data at the end of the function, and leave the empty array, it sends me the following error:
const BreakfastComponent = () => {
const breakfast = [
{
id: 0,
name: "Sandwich de jamón y queso",
price: '35',
img: "https://i.ibb.co/ynC9xHZ/sandjc.png",
},
{
id: 1,
name: "Jugo Natural",
price: '15',
img: "https://i.ibb.co/8mrd4MK/orangejuice.png",
},
{
id: 2,
name: "Café americano",
price: '20',
img: "https://i.ibb.co/nsj1GL0/americancoffe.png",
},
{
id: 3,
name: "Café con leche",
price: '28',
img: "https://i.ibb.co/GRPBm7j/coffeandmilk.png",
}
];
const [stateProduct, setStateProduct] = useState([ ]);
useEffect(() => {
setStateProduct(breakfast);
}, [breakfast]);
return (
<section className="databreakfast">
{stateProduct.map((element, item) =>
<ButtonsBComponent
key={item}
{...element}
/>
)}
</section>
)
};
export default BreakfastComponent;
React Hook useEffect has a missing dependency: 'breakfast'. Either include it or remove the dependency array react-hooks/exhaustive-deps

The problem is simple, you have breakfast array as a dependency to useEffect and in useEffect you are setting the breakfast array itself. Now since the const breakfast array is declared inside the component, a new reference to it is generated everytime and since useEffect sets the array in state, it re-renders and on next re-render the comparison for breakfast array fails since the reference has changed.
The solution is simple, you don't need to have the const array in the component and also you don't need to use useEffect
const breakfast = [
{
id: 0,
name: "Sandwich de jamón y queso",
price: '35',
img: "https://i.ibb.co/ynC9xHZ/sandjc.png",
},
{
id: 1,
name: "Jugo Natural",
price: '15',
img: "https://i.ibb.co/8mrd4MK/orangejuice.png",
},
{
id: 2,
name: "Café americano",
price: '20',
img: "https://i.ibb.co/nsj1GL0/americancoffe.png",
},
{
id: 3,
name: "Café con leche",
price: '28',
img: "https://i.ibb.co/GRPBm7j/coffeandmilk.png",
}
];
const BreakfastComponent = () => {
const [stateProduct, setStateProduct] = useState(breakfast);
return (
<section className="databreakfast">
{stateProduct.map((element, item) =>
<ButtonsBComponent
key={item}
{...element}
/>
)}
</section>
)
};
export default BreakfastComponent;

useEffect's second argument is an array of state/hooks to be watched and when they change, to run the effect. Since your breakfast is a const, I'm guessing you just want the original stateProduct to be breakfast. So instead of using [] as the default, use breakfast.
const [stateProduct, setStateProduct] = useState(breakfast);
Also, probably a good idea to declare const breakfast ... outside of your react functional component so it doesn't re-declare it on every re-render.

Related

update complex array react js useState

I have a long array. In this array I want to update the qty which is under misc array
I have a list of a person, let's say person with index 0 and index 1, each person can have misc with index 0, and index 1 and each misc can have array with index 0 and 1 and I want to update the qty of misc array.
Here is an example: https://playcode.io/1028032
import React from 'react';
import { useState } from 'react';
export function App(props) {
const[persons,setPersons] = useState([
{
id: 1,
name: "john",
gender: "m",
misc: [
{
id: 1,
name: "xxx",
qty: 1
},
{
id: 2,
name: "xxx1",
qty: 1
}
]
},
{
id: 2,
name: "mary",
gender: "f",
misc: [
{
id: 1,
name: "aaa",
qty: 1
},
{
id: 2,
name: "bbb",
qty: 1
}
]
},
]
)
const updatePersonMiscQty = (personIndex, miscIndex) => {
setPersons(persons => {
const miscItem = persons[personIndex]?.misc?.[miscIndex]
if (miscItem ) {
miscItem.qty += 1;
}
return [...persons];
})
}
return (
<div className='App'>
<h1>Hello React.</h1>
<h2>Start editing to see some magic happen!</h2>
<a href="" onClick={()=>updatePersonMiscQty(0,0)}>Click</a>
{console.log(persons)}
</div>
);
}
Let's say I passed 0,0 in updatePersonMiscQty(), first 0 is personIndex, and second 0 is miscIndex. so now it should update qty of person with index 0 and misc with index 0. This array. But nothing is rendered.
{
id: 1,
name: "john",
gender: "m",
misc: [
{
id: 1,
name: "xxx",
qty: 2
},
This is reloading the page:
<a href="" onClick={()=>updatePersonMiscQty(0,0)}>Click</a>
You can prevent this by setting the href to "#".
<a href="#" onClick={()=>updatePersonMiscQty(0,0)}>Click</a>
Or you probably want to use a button instead.
<button onClick={()=>updatePersonMiscQty(0,0)}>Click</button>
you just need to change <a> tag to <button> but I also would recommend to create deeply nested copies when trying to update a nested object.
Try immer.js, with it you just specify what has changed at any level of nesting and it will automatically take care of all the changes that need to be handled.https://immerjs.github.io/immer/

Multidimensional use of .map() array function in Javascript

I'm currently stuck in trying to transform one array to another using an array of headers as a key variable. Somewhat confusing to explain so I think the code might be better.
// Array received from backend
const projects = [
{id: 0001, name: "Test Project 1", projectType: "Quant"},
{id: 0002, name: "Test Project 2", projectType: "Qual"},
{id: 0003, name: "Test Project 3", projectType: "Quant"},
];
I use the first object of the array to create a new array for the headers using Object.keys(projects[0]).. So far so good
I would like to use the DataGrid from Material UI which requires a specific format for their rows and columns data structures. See below.
const columns-MUI = [
{
field: 'firstName',
headerName: 'First name',
width: 150,
editable: true,
},
{
field: 'lastName',
headerName: 'Last name',
width: 150,
editable: true,
},
];
const rows-MUI = [
{ id: 1, lastName: 'Snow', firstName: 'Jon', age: 35 },
{ id: 2, lastName: 'Lannister', firstName: 'Cersei', age: 42 },
{ id: 3, lastName: 'Lannister', firstName: 'Jaime', age: 45 },
{ id: 4, lastName: 'Stark', firstName: 'Arya', age: 16 },
]
I've managed to convert the headers from the result of Object.keys(projects[0]) to the desired column structure as defined by in columns-MUI with the following code when setting the state in a useEffect hook:
setNewColumns(tableHeaders?.map(( header, index ) => {
return {
field: index,
headerName: header,
width: 90
}
}));
Where I'm stuck is transforming the array from projects to rows-MUI because I somehow need to create both the key and value pair dynamically. This is what I've resulted in so far:
let tempProjectsList = [];
projectsList?.map(( project, index) => {
let projectObject = ''
tableHeaders.map(( header, index ) => {
const value = project[header] != null ? `"${project[header]}"` : ""
projectObject += `${header}:${value},`;
})
const parsedObject =`{${projectObject}}`;
tempProjectsList.push(parsedObject);
})
This process treats the key value pair as a string and parses the object once the iteration through the tableHeaders array is complete. There must be a more elegant solution though that I'm missing so I'd much appreciate the help.
I am certain that the "more elegant" solution begins like this but I think I reach the limits of my knowledge of Javascript arrays at this point:
setNewRows(projectsList?.map(( project, index) => {
return tableHeaders.map(( header, index) => {
})
}));
Let me know if this explanation isn't clear and I'll work on supplying demo of some description.

unable to destructuring two objects simultaneously

How can I map movies by using columns as a property reference.
Like
{movies.map(item => {columns.map(column => item.column.path)})}
but using this i'm getting result as undefined
Movies contains all details about movies
const movies = [
{
_id: "5b21ca3eeb7f6fbccd471815",
title: "Terminator",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 6,
dailyRentalRate: 2.5,
publishDate: "2018-01-03T19:04:28.809Z",
liked: true,
},
{
_id: "5b21ca3eeb7f6fbccd471816",
title: "Die Hard",
genre: { _id: "5b21ca3eeb7f6fbccd471818", name: "Action" },
numberInStock: 5,
dailyRentalRate: 2.5,
},
{
_id: "5b21ca3eeb7f6fbccd471817",
title: "Get Out",
genre: { _id: "5b21ca3eeb7f6fbccd471820", name: "Thriller" },
numberInStock: 8,
dailyRentalRate: 3.5,
},
{
_id: "5b21ca3eeb7f6fbccd471819",
title: "Trip to Italy",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5,
},
{
_id: "5b21ca3eeb7f6fbccd47181a",
title: "Airplane",
genre: { _id: "5b21ca3eeb7f6fbccd471814", name: "Comedy" },
numberInStock: 7,
dailyRentalRate: 3.5,
}];
Columns contains all properties that are needed to access the movies
const columns = [
{ path: "title", label: "Title" },
{ path: "genre", label: "Genre" },
{ path: "numberInStock", label: "Stock" },
{ path: "dailyRentalRate", label: "Rate" }];
I know this problem can be solved using 2 loops.
1 outer loop for movies after getting each movie, use a 2nd internal loop to access the properties
Update: After getting #rory-mccrossan answer
Further, how can I map this data in a table such that
This is the code part regarding the same that I used.
import React, { Component } from "react";
import _ from "lodash";
class TableBody extends Component {
renderCell = (item, column) => {
if (column.content) return column.content(item);
return _.get(item, column.path);
};
createKey = (item, column) => {
return item._id + (column.path || column.key);
};
render() {
const { data, columns } = this.props;
return (
<tbody>
{data.map((item) => (
<tr key={item._id}>
{columns.map((column) => (
<td key={this.createKey(item, column)}>
{this.renderCell(item, column)}
</td>
))}
</tr>
))}
</tbody>
);
}
}
export default TableBody;
But I'm getting the error: Objects are not valid as a React child (found: object with keys {_id, name}). If you meant to render a collection of children, use an array instead.)
The main issue is that how can I map data in a table
There's two issues here. Firstly the braces around the second map() call will be interpreted as a code block, yet it does not return any value so the resulting array will be empty. Remove those braces.
Secondly, item.column.path needs to be item[column.path] as you're using column.path as the property accessor of item.
Here's a working example:
const movies = [{_id:"5b21ca3eeb7f6fbccd471815",title:"Terminator",genre:{_id:"5b21ca3eeb7f6fbccd471818",name:"Action"},numberInStock:6,dailyRentalRate:2.5,publishDate:"2018-01-03T19:04:28.809Z",liked:!0},{_id:"5b21ca3eeb7f6fbccd471816",title:"Die Hard",genre:{_id:"5b21ca3eeb7f6fbccd471818",name:"Action"},numberInStock:5,dailyRentalRate:2.5},{_id:"5b21ca3eeb7f6fbccd471817",title:"Get Out",genre:{_id:"5b21ca3eeb7f6fbccd471820",name:"Thriller"},numberInStock:8,dailyRentalRate:3.5},{_id:"5b21ca3eeb7f6fbccd471819",title:"Trip to Italy",genre:{_id:"5b21ca3eeb7f6fbccd471814",name:"Comedy"},numberInStock:7,dailyRentalRate:3.5},{_id:"5b21ca3eeb7f6fbccd47181a",title:"Airplane",genre:{_id:"5b21ca3eeb7f6fbccd471814",name:"Comedy"},numberInStock:7,dailyRentalRate:3.5}];
const columns = [{path:"title",label:"Title"},{path:"genre",label:"Genre"},{path:"numberInStock",label:"Stock"},{path:"dailyRentalRate",label:"Rate"}];
let output = movies.map(item => columns.map(column => item[column.path]));
console.log(output);

Using map on array of objects results in a errormessage

I am trying to figure out why this simple code does not work, it feels like I have done exactly the same thing many times before without errors. What have I done wrong?
import React, {useState} from 'react'
import { MenuItem } from '../menu-item/menu-item.component'
import './directory.styles.scss'
export const Directory = () => {
const sections = [
{
title: 'hats',
imageUrl: 'https://i.ibb.co/cvpntL1/hats.png',
id: 1,
linkUrl: 'shop/hats'
},
{
title: 'jackets',
imageUrl: 'https://i.ibb.co/px2tCc3/jackets.png',
id: 2,
linkUrl: 'shop/jackets'
},
{
title: 'sneakers',
imageUrl: 'https://i.ibb.co/0jqHpnp/sneakers.png',
id: 3,
linkUrl: 'shop/sneakers'
},
{
title: 'womens',
imageUrl: 'https://i.ibb.co/GCCdy8t/womens.png',
size: 'large',
id: 4,
linkUrl: 'shop/womens'
},
{
title: 'mens',
imageUrl: 'https://i.ibb.co/R70vBrQ/men.png',
size: 'large',
id: 5,
linkUrl: 'shop/mens'
}
];
return (
<div className="directory-menu">
{sections.map((title, imageUrl, id) => <MenuItem key={id} title={title} imageUrl={imageUrl}/>)}
</div>
)
}
Error Message:
Error: Objects are not valid as a React child (found: object with keys
{title, imageUrl, id, linkUrl}). If you meant to render a collection
of children, use an array instead.
You are not destructuring properly i.e.
.((title, imageUrl, id) =>
should be
.(({title, imageUrl, id}) =>
Check out this post, it's a common React error.
You could also try something like this
Object.entries(sections).map((x)=>
return x.title;
})

props.array.map is not a function

I have a few components that are to render dynamically based on values passed through props. My main component(1) is dynamically creating its subcomponents(2) and passing values into those components via props. Each component(2) created is then supposed to utilize the values in props to create new subcomponents(3) of this component.
The problem arises when I try to map the object array passed as props in component 2. Component 1 successfully creates components 2, but component 2 displays the error "this.props.section.map is not a function". I'm using the exact code from component one, the only difference is I am referencing the array via props.
this.state = {
menuSections: [
{
id: 1, title: 'Starters', menuItem: [{
id: 1,
name: 'Meatball',
description: 'Large Meatball',
price: '11 meatballs'
}]
},
{
id: 2, title: 'Starters', menuItem: [{
id: 2,
name: 'Meatball',
description: 'Large Meatball',
price: '11 meatballs'
}]
},
{
id: 3, title: 'Starters', menuItem: [{
id: 3,
name: 'Meatball',
description: 'Large Meatball',
price: '11 meatballs'
}]
},
],
{this.state.menuSections.map(menuSection => (<MenuSection
section={menuSection} mobileMode={this.state.mobileMode} />))}
This is the code from my App component, which works like a charm. the code for my 2nd component (MenuSection) is where the problem arises.
{this.props.section.map((itemContainer, i) => (<ItemContainer styles={styles}
image={imageFood1} title={itemContainer.menuItem[i].name}
description={itemContainer.menuItem[i].description}
price={itemContainer.menuItem[i].price} />))}
Ideally, the section.map function in the 2nd component will render each individual menu item based on the props passed from the Main app. Instead, I receive the error "this.props.section.map is not a function".
edit: I know variations of this question have been asked before, but I have tried numerous suggestions, none of which have worked.
this.props.section is going to be an Object like :
{
id: 1, title: 'Starters', menuItem: [{
id: 1,
name: 'Meatball',
description: 'Large Meatball',
price: '11 meatballs'
}]
}
So you can't .map it ,
either you pass the array :
{
this.state.menuSections.map(menuSection => (
<MenuSection
section={menuSection.menuItem}
id={menuSection.id}
title={menuSection}
mobileMode={this.state.mobileMode}
/>
))
}
Or, you map the array inside :
{
this.props.section.menuItem.map((itemContainer, i) => (
<ItemContainer
styles={styles}
image={imageFood1} title={itemContainer.menuItem[i].name}
description={itemContainer.menuItem[i].description}
price={itemContainer.menuItem[i].price}
/>
))
}
Section is an Object.
this.props.section.menuItem.map()

Categories

Resources