2

I'm a beginner and want to write a small warehousing app. A click on the take or add button in the listItem component should call the addAmount or takeAmount functions and change the amount-value of the listItem. console.log provides the right value-change but the number in the UI isn't changing. Can you help me?

App component:

import React, { useState } from 'react';
import logo from './logoblank.svg';
import './App.css';
import { AccessAlarm, AccountBox, LocalCafe, Print } from '@material-ui/icons';
import ListItem from './ListItem/ListItem.js';
import Location from './Location/Location.js';
import Category from './Category/Category.js';
import Search from './Search/Search.js'

const App = props => {
  const [locationState, setLocationState] = useState({
    locations: [
      { name: '', picture: '' }
    ]
  });

  let [listState, setListState] = useState({
    listItems: [
      { key: '', icon: <LocalCafe />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 3, orderAction: 'xxx' },
      { key: '', icon: <Print />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 5, orderAction: 'xxx' }
    ]
  });

  let [searchState, setSearchState] = useState({value: ''});

  console.log(locationState, listState);

  const listItems = (
    <div>
      {listState.listItems.map((listItem, index) => {
        return <ListItem
          key={index}
          icon={listItem.icon}
          name={listItem.name}
          details={listItem.details}
          storage={listItem.storage}
          amount={listItem.amount}
          click1={() => addAmount(index)}
          click2={() => takeAmount(index)}/>
      })}
    </div>
  )

  let addAmount = (index) => {
    let amount = listState.listItems[index].amount;

    amount = amount + 1;

    let listCopy = listState;

    listCopy.listItems[index].amount = amount;

    setListState(listCopy);

    console.log(listState.listItems[index].amount);

    console.log(listState);
  }

  let takeAmount = (index) => {
    let amount = listState.listItems[index].amount;

    amount = amount - 1;

    let listCopy = listState;

    listCopy.listItems[index].amount = amount;

    setListState(listCopy);

    console.log(listCopy.listItems[index].amount);
  }

  let searchChangeHandler = (event) => {
    let searchValue = searchState;
    searchValue.value = event.target.value;
    setSearchState(searchValue);

    console.log(searchState);
  }


  return (
    <div className="App">
      <header className="App-header">
        <div className="App-header-left">
          <img src={logo} className="App-logo" alt="SleevesUp!" />
          <p className="pad">Warehousing</p>
        </div>
        <div className="App-header">
          <Search
          changed={(event) => searchChangeHandler(event)} />
        </div>
        <div className="App-header-right">
          <p className="pad">Roomhero</p>
          <AccountBox />
        </div>
      </header>
      <Location
        name={locationState.locations[0].name}
        picture={locationState.locations[0].picture}
      />
      <Category />
      {listItems}
    </div>
  );
}

export default App;

Here is the code of the ListItem.js

import React from 'react';
import './ListItem.css'

const listItem = (props) => {
    return (
        <div className="listItem">
            <div className="listItemColumn icon">
                {props.icon}
            </div>
            <div className="listItemColumn">
                <div className="name">{props.name}</div>
                <div className="details">{props.details}</div>
            </div>
            <div className="listItemColumn">
                {props.storage}
            </div>
            <div className="listItemColumnTake">
                <button className="take" onClick={props.click2}>Take</button>{props.amount}<button className="take" onClick={props.click1}>Add</button>
            </div>
            <div className="listItemColumn">
                <button className="order">Order</button>
            </div>
        </div>
    )
};

export default listItem;
demiurge
  • 23
  • 3

2 Answers2

1

You are copying the reference of the state to listCopy, hence directly mutating the state.

This won't cause re-render.

Instead create a new object by spreading the current object and it should work fine.

let listCopy = {...listState};
kooskoos
  • 4,622
  • 1
  • 12
  • 29
  • Thank you! Unfortunately that gives me the following error: `code` TypeError: undefined is not a function addAmount src/App.js:49 46 | 47 | amount = amount + 1; 48 | > 49 | let listCopy = [...listState]; | ^ 50 | 51 | listCopy.listItems[index].amount = amount; 52 | – demiurge Jan 31 '20 at 19:32
  • Got it! Thank you very much. This is the answer: `code` let listCopy = {...listState}; – demiurge Jan 31 '20 at 20:07
1
import React, { useState } from 'react';
import logo from './logoblank.svg';
import './App.css';
import { AccessAlarm, AccountBox, LocalCafe, Print } from '@material-ui/icons';
import ListItem from './ListItem/ListItem.js';
import Location from './Location/Location.js';
import Category from './Category/Category.js';
import Search from './Search/Search.js'

const App = props => {
  const [locationState, setLocationState] = useState({
    locations: [
      { name: '', picture: '' }
    ]
  });

  let [listState, setListState] = useState({
    listItems: [
      { key: '', icon: <LocalCafe />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 3, orderAction: 'xxx' },
      { key: '', icon: <Print />, name: 'xxx', details: 'xxx', storage: 'xxx', amount: 5, orderAction: 'xxx' }
    ]
  });

  let [searchState, setSearchState] = useState({value: ''});

  console.log(locationState, listState);

  const listItems = (list) => (
    <div>
      {list.listItems.map((listItem, index) => {
        return <ListItem
          key={index}
          icon={listItem.icon}
          name={listItem.name}
          details={listItem.details}
          storage={listItem.storage}
          amount={listItem.amount}
          click1={() => addAmount(index)}
          click2={() => takeAmount(index)}/>
      })}
    </div>
  )

  let addAmount = (index) => {
    let amount = listState.listItems[index].amount;

    amount = amount + 1;

    let listCopy = listState;

    listCopy.listItems[index].amount = amount;

    setListState(listCopy);

    console.log(listState.listItems[index].amount);

    console.log(listState);
  }

  let takeAmount = (index) => {
    let amount = listState.listItems[index].amount;

    amount = amount - 1;

    let listCopy = listState;

    listCopy.listItems[index].amount = amount;

    setListState(listCopy);

    console.log(listCopy.listItems[index].amount);
  }

  let searchChangeHandler = (event) => {
    let searchValue = searchState;
    searchValue.value = event.target.value;
    setSearchState(searchValue);

    console.log(searchState);
  }


  return (
    <div className="App">
      <header className="App-header">
        <div className="App-header-left">
          <img src={logo} className="App-logo" alt="SleevesUp!" />
          <p className="pad">Warehousing</p>
        </div>
        <div className="App-header">
          <Search
          changed={(event) => searchChangeHandler(event)} />
        </div>
        <div className="App-header-right">
          <p className="pad">Roomhero</p>
          <AccountBox />
        </div>
      </header>
      <Location
        name={locationState.locations[0].name}
        picture={locationState.locations[0].picture}
      />
      <Category />
      {listItems(listState)}
    </div>
  );
}

export default App;

Try this out. The listitems in the render won't update since it is not directly connected with the listState state. With my change it should re-render when listState changes.

Lucas Claude
  • 331
  • 1
  • 7