Home > Enterprise >  How to lift a REACT state?
How to lift a REACT state?

Time:04-11

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!

  • Related