all.
I'm learning REACT and came upon a problem.
Basically, I want to move an "handle state" function to the parent component (App), and call it inside the Child component (MenuItem).
In the App Component I create a function "handleClickFavorite" that handles the state of the variable "isFavorite".
In the MenuItem Component I pass both the function and the variable as props and use them in a onClick event. Basically, I want to change between two CSS classes (Favorite and NotFavorite) of the item everytime I click the button or div.
The MenuList part just takes the elements of an array , which are rendered individually in MenuItem, and maps them
App component:
import React, { useState } from 'react';
import './App.css';
import MenuList from './components/MenuList';
import foodItems from './components/data.js';
const App = (props) => {
const [isFavorite, setIsFavorite] = useState(props.isFavorite);
const handleClickFavorite = () => {
setIsFavorite(!isFavorite);
};
return (
<div>
<h1>Wild Restaurant Menu</h1>
<MenuList
isFavorite={isFavorite}
handleClickFavorite={handleClickFavorite}
foodItems={foodItems}
/>
</div>
);
};
export default App;
MenuList Component:
import React from 'react';
import MenuItem from './MenuItem';
function MenuList({ foodItems }) {
console.log(foodItems);
return (
<div>
{foodItems.map((element, index) => (
<MenuItem {...element} key={index} />
))}
</div>
);
}
export default MenuList;
MenuItem Component:
import React, { useState } from 'react';
import '../App.css';
function MenuItem(props) {
//create a state isFavorite that has the inital value of isFavorite that comes from the props
const {
itemName,
description,
foodImage,
price,
isFavorite,
handleClickFavorite,
} = props;
return (
<div className="itemContainer">
<div className="leftContainer">
<div className="imgContainer">
{/* the image will receive the url src from the props */}
<img src={foodImage} alt="" />
</div>
<div className="itemDescription">
{/* the h3 will receive the item name from the props */}
<h3>{itemName}</h3>
{/* the p will receive the item description from the props */}
<p>{description}</p>
</div>
</div>
<div className="rightContainer">
{/* the div will receive the item price from the props */}
<div>{price} EUR</div>
{/* the div with id favorite will have 2 attributes:
- onClick, will call the method handleClickFavorite,
- classname, that will be conditionally rendered, depending on the value of isFavorite from the component's state
*/}
<div
id="favorite"
onClick={handleClickFavorite}
className={isFavorite ? 'isFavorite' : 'notFavorite'}
/>
</div>
</div>
);
}
export default MenuItem;
It's my first time asking a question here, so pls be gentle. I tried many soluctions, but unfortunately nothing is working. The state of isFavorite doesn't change, and neither the classes.
Can someone help me?
CodePudding user response:
The reason why you code works no such as you want because you have not written props to MenuItem.
You have done right writing props to MenuList, so you need to dispatch useState isFavorite and setIsFavorite from MenuList to MenuItem the same way.
Just write in function MenuList({ foodItems })
also isFavorite and setIsFavorite.
So it will be function MenuList({ foodItems, isFavorite, setIsFavorite })
and dispatch them as props to MenuItem, as you have done to MenuList.
Some note. If you don't like 2-level props drilling, you can pat attention to state managers (redux, mob x, effector) or to hook useContext. So you can get data among all your project without props.
CodePudding user response:
You didn't actually pass props into children. In your MenuList
component, you have only foodItems
as props. So you can't get other props what you need. These are isFavorite
and handleClickFavorite
.
Your code block and change.
function MenuList({ foodItems }) {
console.log(foodItems);
return (
<div>
{foodItems.map((element, index) => (
<MenuItem {...element} key={index} />
))}
</div>
);
}
// Updated code
function MenuList({ foodItems, isFavorite, handleClickFavorite}) {
console.log(foodItems);
return (
<div>
{foodItems.map((element, index) => (
<MenuItem {...element} key={index}
isFavorite={isFavorite}
handleClickFavorite={handleClickFavorite}
/>
))}
</div>
);
}
Additionally, if you have a tree with many depths, you need to pass all props for all children at each level. This may be irritating and reduce the code quality in terms of performance and code readability.
In this case, you can use React context API or 3rd party libraries such as redux.
And you spread element
into props by {...element}
in MenuItem
component in <MenuItem {...element} key={index} />
.
But I think this may be not good because MenuItem may have more props or data in practice. So I recommend to use it like <MenuItem key={index} element={element} />
.
Then you can access element
in MenuItem like follows.
function MenuItem(props) {
//create a state isFavorite that has the inital value of isFavorite that comes from the props
const {
element: {
itemName,
description,
foodImage,
price,
},
isFavorite,
handleClickFavorite,
} = props;
...
}
Hope this will be helpful!