Home > Blockchain >  Cannot update object via useState
Cannot update object via useState

Time:11-28

I am new to React and learning about states and props. I am following a React Wes Bos course and the teacher is using class components, so I am sort of refactoring as I go along to functional component (for exercise and because I have to learn those).

We are coding an app that is supposed to be a fish restaurant, and at some point, we want to load to the order section some values.

I have two main problems:

1 - When I try to run the method addToOrder(key) manually in the React dev tool by using $r on App.js, I get an error VM761:1 Uncaught TypeError: $r.addToOrder is not a function

2 - The second issue is that when I click on the button Add To Order, the one that is supposed to update the order{} object, the order object itself does not get updated.

I have been searching for a good half day now and I am not sure what could be wrong.

As a self-check:

  • the prop index is passed correctly from to as I can console.log(index) and do get the current one.

I am sorry if I am not explaining myself properly, it's a bit hard to condense into a short post. Do ask questions and clarifications as needed, I'll do my best to provide the correct info.

Here's the two components code:

App

import React from "react";
import { Header } from "./Header";
import { Order } from "./Order";
import { Inventory } from "./Inventory";
import { useState } from "react";
import sampleFishes from "../sample-fishes";
import { Fish } from "./Fish";

export const App = () => {
  const [state, setState] = useState({
    fishes: {},
    order: {},
  });

  /**
   * Structure of the function served in <AddFishForm>
   * Making a copy of the state to avoid mutations ...state.fishes
   * Date.now() used to assign a unique key
   *
   */
  const addFish = (fish) => {
    const fishes = { ...state.fishes };
    fishes[`fish${Date.now()}`] = fish;
    setState({
      fishes: fishes,
    });
  };

  /**
   * Function to display a sample fishes in the list
   * Made to avoid manual typing
   * Fish data comes from ../sample-fishes
   */
  const loadSampleFishes = () => {
    setState({ fishes: sampleFishes });
  };

  /**
   * Take a copy of state
   * Either add to the order or update the number in order
   * (if order exists, adds one to it, if not, set it to one)
   * Call setState() to update state object
   */
  const addToOrder = (key) => {
    const order = { ...state.order };
    order[key] = order[key]   1 || 1;
    setState({
      order: order,
    });
  };

  return (
    <div className="catch-of-the-day">
      <div className="menu">
        <Header tagline="Fresh Seafood Market" />
        <ul className="fishes">
          {Object.keys(state.fishes).map((key) => {
            return (
              <Fish
                key={key}
                details={state.fishes[key]}
                addToOrder={addToOrder}
              ></Fish>
            );
          })}
        </ul>
      </div>
      <Order />
      <Inventory addFish={addFish} loadSampleFishes={loadSampleFishes} />
    </div>
  );
};

Fish

import React from "react";
import { formatPrice } from "../helpers";

export const Fish = ({ details, addToOrder, index }) => {
  const isAvailable = details.status === "available";

  const handleClick = () => {
    addToOrder[index];
  };

  return (
    <li className="menu-fish">
      <img src={details.image} alt="" />
      <h3 className="fish-names">
        {details.name}
        <span className="price">{formatPrice(details.price)}</span>
      </h3>
      <p>{details.desc}</p>
      <button type="submit" disabled={!isAvailable} onClick={() => handleClick}>
        {isAvailable ? "Add to order" : "Sold out!"}
      </button>
    </li>
  );
};

calling the function from $r

CodePudding user response:

The onClick={() => handleClick} should be

either with parenthesis at the end to call it

onClick={() => handleClick()} 

or better yet, pass it directly as the callback method

onClick={handleClick}

CodePudding user response:

First welcome to react world Jim Halpert! Hope you are enjoying your journey.

Their a couple of issues I have found in your example.

1)In the Fish.jsx click handler you need to pass the index while calling the actual function.

2)You have to bind the index as props, from the parent JSX file.

3)Since you have order and fishes in the same object you will have to copy the previous data as well as shown in line 57 of App.jsx

I have tweaked your example a bit, have a look at it below:

import React from "react";
import { useState } from "react";
import { Fish } from "./Fish";

const App = () => {
  const [state, setState] = useState({
    fishes: {
      "salmon" : {
        "image":"https://via.placeholder.com/20x20",
        "name":"salmon",
        "description":"test",
        "status":"available"
      }
    },
    order: {},
  });

  /**
   * Structure of the function served in <AddFishForm>
   * Making a copy of the state to avoid mutations ...state.fishes
   * Date.now() used to assign a unique key
   *
   */
  const addFish = (fish) => {
    const fishes = { ...state.fishes };
    fishes[`fish${Date.now()}`] = fish;
    setState({
      fishes: fishes,
    });
  };

  /**
   * Function to display a sample fishes in the list
   * Made to avoid manual typing
   * Fish data comes from ../sample-fishes
   */
  const loadSampleFishes = () => {
    setState({ fishes: sampleFishes });
  };

  /**
   * Take a copy of state
   * Either add to the order or update the number in order
   * (if order exists, adds one to it, if not, set it to one)
   * Call setState() to update state object
   */
  const addToOrder = (fish) => {
    
    const order = { ...state.order };
    order[fish] = order[fish.key]   1 ?? 1;
    console.log("Order >>>>>>>>>>>> " JSON.stringify(order));
    setState({
      ...state,
      order: order,
    });
  };

  return (
    <div className="catch-of-the-day">
      <div className="menu">
        
        <ul className="fishes">
           {Object.keys(state.fishes).map((key) => {
            return (
             <Fish
                index={key}
                details={state.fishes[key]}
                addToOrder={addToOrder}
              ></Fish>
            );
          })}
        </ul>
      </div>
      
    </div>
  );
};

export default App;

import React from "react";
export const Fish = ({ details, addToOrder, index }) => {
  const isAvailable = details.status === "available";

  const handleClick = (index) => {
    addToOrder(index);
  };

  return (
   <li className="menu-fish">
      <img src={details.image} alt="" />
      <h3 className="fish-names">
        {details.name}
        <span className="price">{details.price}</span>
      </h3>
      <p>{details.desc}</p>
      <button type="submit" disabled={!isAvailable} onClick={() => handleClick(index)}>
        {isAvailable ? "Add to order" : "Sold out!"}
      </button>
      
    </li>
  );
};
  • Related