1

I am learning ReactJs and I am trying to implement below react classs based component into functional component but I am having difficulties in it. When I implement this into functional component it does not updates the webpage.

I have imported DISHES an object from where I gets the data. I am trying to set state using functional component

Below I have attached some code which I tried to use to set state

   class Main extends Component {
            constructor(props) {
                super(props);
                this.state = {
                    dishes: DISHES,
                    selectedDish: null,
                };
            }
        
            onDishSelect(dishId) {
                this.setState({ selectedDish: dishId });
            }
        
            render() {
                return (
                    <div>
                        <Navbar dark color="primary">
                            <NavbarBrand href="./" className="mx-5">
                                Ristorante De Confusion
                            </NavbarBrand>
                        </Navbar>
                        <Menu dishes={this.state.dishes} onClick={(dishId) => this.onDishSelect(dishId)} />
                        <Dishdetail
                            dish={this.state.dishes.filter((dish) => dish.id === this.state.selectedDish)[0]}
                        />
                    </div>
                );
            }
        }
        
        export default Main;
  

This is I am trying to convert

import React, { useState } from "react";
import { Navbar, NavbarBrand } from "reactstrap";
import Menu from "./Menu";
import Dishdetail from "./Dishdetail";
import { DISHES } from "../shared/dishes";

function Main() {
    const [dishes] = useState({ DISHES });
    const [selectedDish, updatedDish] = useState(null);

    function onDishSelect(dishId) {
        return updatedDish((selectedDish) => ({
            ...selectedDish,
            selectedDish: dishId,
        }));
    }

    return (
        <div>
            <Navbar dark color="primary">
                <NavbarBrand href="./" className="mx-5">
                    Ristorante De Confusion
                </NavbarBrand>
            </Navbar>
            <Menu dishes={dishes} onClick={(dishId) => onDishSelect(dishId)} />
            <Dishdetail dish={dishes.filter((dish) => dish.id === selectedDish)[0]} />
        </div>
    );
}

export default Main;

4 Answers4

1

Issue

It seems the issue is in the onDishSelect handler, it is nesting the dish id in a nested property.

const [selectedDish, updatedDish] = useState(null);

function onDishSelect(dishId) {
  return updatedDish((selectedDish) => ({
    ...selectedDish,
    selectedDish: dishId, // <-- nested
  }));
}

And doesn't correctly access into the nested property when rendering to filter the dishes array.

state.dishes.filter((dish) => dish.id === state.selectedDish)[0]

Solution

Just set the selectedDish to the passed dishId value.

const [selectedDish, updatedDish] = useState(null);

function onDishSelect(dishId) {
  updatedDish(dishId);
}

Instead of Array.prototypt.filter which returns an array that you need to access the 0th element of, use Array.prototype.find to find and return the matching element object.

state.dishes.find((dish) => dish.id === state.selectedDish)

There's also no need to store the DISHES array in local state, just reference it directly in the component.

import React, { useState } from "react";
import { Navbar, NavbarBrand } from "reactstrap";
import Menu from "./Menu";
import Dishdetail from "./Dishdetail";
import { DISHES } from "../shared/dishes";

function Main() {
  const [selectedDish, updatedDish] = useState(null);

  function onDishSelect(dishId) {
    updatedDish(dishId);
  }

  return (
    <div>
      <Navbar dark color="primary">
        <NavbarBrand href="./" className="mx-5">
          Ristorante De Confusion
        </NavbarBrand>
      </Navbar>
      <Menu
        dishes={DISHES}
        onClick={(dishId) => onDishSelect(dishId)}
      />
      <Dishdetail dish={DISHES.find((dish) => dish.id === selectedDish)} />
    </div>
  );
}
Drew Reese
  • 165,259
  • 14
  • 153
  • 181
0

The problem is that you don't fully understand the useState hook. It essentially provides you with 2 variables:

The state variable itself
A set method for the state variable

Here is how you'd actually set up your state variables:

const [dishes, setDishes] = useState(DISHES)
const [selectedDish, setSelectedDish] = useState(null)

const selectADish = dishId => setSelectedDish(dishId)

As for accessing state, you no longer write

this.state.dishes

rather you just write dishes as that is the variable within your function component scope.

no_modules
  • 117
  • 7
0

With what minimal e.g. you have given it would be as so, edit the props as required and used ...

function Main(props) {
  const [state, setState] = useState({
    selectedDish: null,
    dishes: DISHES, // it's not clear from where it is referred `props.Dishes` if from props
  });

  function onDishSelect(dishId) {
    setState({ ...state, selectedDish: dishId });
  }

  return (
    <div>
      <Navbar dark color="primary">
        <NavbarBrand href="./" className="mx-5">
          Ristorante De Confusion
        </NavbarBrand>
      </Navbar>
      <Menu dishes={state.dishes} onClick={(dishId) => onDishSelect(dishId)} />
      <Dishdetail
        dish={state.dishes.filter((dish) => dish.id === state.selectedDish)[0]}
      />
    </div>
  );
}

export default Main;
KcH
  • 3,302
  • 3
  • 21
  • 46
  • Sorry for inconvenience. DISHES is coming from dishes.js file where I have created an object containing all the id, author name, comments, description sort of things. I am importing that as DISHES and trying to use dishes. So in that case I can use dishes directly. I have also come to understand that if I am not altering state then I can use that directly. But again thanks for helping. – Prince Singh Oct 20 '22 at 07:52
0

Some things to unpack here:

  • It doesn't render on screen because you need to return the JSX
  • When instantiating the state dishes you don't need to wrap the data in an object
  • Do you need dishes-state? Currently, you are not altering the state, so you could just use DISHES

With those changes, here's what the code could look like:

  import React, { useState } from "react";
  import { Navbar, NavbarBrand } from "reactstrap";
  import Menu from "./Menu";
  import Dishdetail from "./Dishdetail";
  import { DISHES } from "../shared/dishes";

  function Main() {
    const [selectedDish, setSelectedDish] = useState(null);
    return (
      <div>
          <Navbar dark color="primary">
              <NavbarBrand href="./" className="mx-5">
                  Ristorante De Confusion
              </NavbarBrand>
          </Navbar>
          <Menu dishes={DISHES} onClick={dishId => setSelectedDish(dishId)} />
          <Dishdetail
              dish={DISHES.filter(dish => dish.id === selectedDish)[0]}
          />
      </div>
    );
  }

  export default Main;
Fralle
  • 889
  • 6
  • 12
  • Thanks for helping. It is working as expected. Also u helped me to understand that if I am not altering state then I can use that directly. – Prince Singh Oct 20 '22 at 07:48