Nord
Nord

Reputation: 55

Can't update object in state

Help, please, to solve a problem. I tried a huge number of solutions, but nothing worked.

My state

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      countId:{quantityId:[]},
    }}

CountId creates an array of objects in state. Id is taken from another component (no problem with this). To demonstrate what is in id, you can take Product 1,Product 2.... and accordingly this.state.countId.quantityId will be {Product 1, quantity: 1}, {Product 2, quantity: 1}

CountId = (event) => {
    this.setState({
          countId:{
            ...this.state.countId,
            quantityId:
            this.state.countId.quantityId.concat({id:(event.currentTarget.id), quantity: 1})}})
  }

I am using cloneDeep lodash to create a deep copy of an object.

IncrementItem = () => {
  const deepIncrement = cloneDeep(this.state.countId.quantityId);
  this.setState({
    ...this.state.countId.quantityId,
    quantity: deepIncrement.map(i => i.id ).quantity + 1
})}

buttons to increase or decrease the quantity and the quantity output itself

     <button onClick={this.IncrementItem}>+</button>
        <div>{this.state.countId.quantityId.map(c => c.id ? c.quantity: null)}</div>
     <button onClick={this.DecrementItem}>-</button>

More code

App.js

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      id: [],
      countId:{quantityId:[]},
    };
  }

  CountId = (event) => {
    this.setState({
          countId:{
            ...this.state.countId,
            quantityId:
            this.state.countId.quantityId.concat({id:(event.currentTarget.id), quantity: 1})}})
  }

  IncrementItem = (id) => {
    const itemFound = this.state.countId.quantityId.find(element => element.id ===id);
  if (itemFound) {
    this.setState({
      ...this.state,
      countId: {...this.state.countId, quantityId: 
        [{...itemFound, quantity: itemFound.quantity + 1 }, 
          ...this.state.countId.quantityId.filter(element => element.id !==id)]}
  })}
  }
DecrementItem = (id) => {
  const itemFound = this.state.countId.quantityId.find(element => element.id ===id);
if (itemFound) {
  this.setState({
    ...this.state,
    countId: {...this.state.countId, quantityId: 
      [{...itemFound, quantity: itemFound.quantity - 1 }, 
        ...this.state.countId.quantityId.filter(element => element.id !==id)]}
})}
}

 render() {
return (
<div>
<BrowserRouter>
<Routes>
<Route path='/store' element={<Store CountId={this.CountId}/>}/>
<Route path='/cart' element={<Cart countId={this.state.countId}/>}/>
</Routes>
</BrowserRouter>
</div>
)}

Store

export default class Store extends Component {
  constructor(props) {
    super(props);
    this.state = {
      products: [
        {
          id: "apple"
        },
        {
          id: "lemon"
        },
        {
          id: "melon"
        }
      ]
    };
  }
  countId = () => {
    if (this.props.CountId) {
      this.props.CountId();
    }
  };
  render() {
    return (
      <div>
        {this.state.products.map((p) => {
          return (
            <div id={p.id} onClick={this.props.countId }>
              CountId 
            </div>
          );
        })}
      </div>
    );
  }
}

Cart

export default class Cart extends Component {
  constructor(props) {
    super(props);
    this.state = {
      products: [
        {
              id: "apple"
            },
            {
              id: "lemon"
            },
            {
              id: "melon"
            }
      ]
    };
  }
  render() {
    return (
      <div>
        {this.state.products.map((p) => {
          return (
            <div>
              <button onClick= 
                     {()=>this.props.IncrementItem(id)} >+</button>
                        <div > 
                     {value.state.countId.quantityId.map(c => c.id ? c.quantity: 1)}</div>
                        <button  onClick= 
                     {this.props.DecrementItem} id={products.id}>-</button>
            </div>
          );
        })}
      </div>
    );
  }
}

Upvotes: 0

Views: 285

Answers (2)

Dheeraj Sharma
Dheeraj Sharma

Reputation: 244

This is your corrected code as per your ask, but i still feel you must correct the function names to make it more concise and readable.

import { Component } from "react";
import { Routes } from "react-router";
// import Store from "./Store";
// import Cart from "./Cart"
import {
  BrowserRouter,
  Link,
  Route,
} from "react-router-dom";
import Home from "./Home";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      id: [],
      countId: { quantityId: [] },
    };
    this.IncrementItem = this.IncrementItem.bind(this);
    this.DecrementItem = this.DecrementItem.bind(this)
  }

  CountId = (event) => {
    const id = event.currentTarget.id
    this.IncrementItem(id)
  }

  IncrementItem = (id) => {
    const itemFound = this.state.countId.quantityId.find(element => element.id === id);
    if (itemFound) {
      const indexOfItemFound = this.state.countId.quantityId.indexOf(itemFound)
      this.setState({
        countId: {
          ...this.state.countId,
          quantityId: [
            ...this.state.countId.quantityId.slice(0, indexOfItemFound),
            { ...itemFound, quantity: itemFound.quantity + 1 },
            ...this.state.countId.quantityId.slice(indexOfItemFound + 1),
          ]
        }
      })
    }
    else {
      this.setState({
        countId: {
          ...this.state.countId,
          quantityId: [
            ...this.state.countId.quantityId,
            { id: id, quantity: 1 }
          ]
        }
      })
    }
  }
  DecrementItem = (id) => {
    const itemFound = this.state.countId.quantityId.find(element => element.id === id);
    if (itemFound) {
      const indexOfItemFound = this.state.countId.quantityId.indexOf(itemFound)
      if (itemFound.quantity > 1) {
        this.setState({
          countId: {
            ...this.state.countId,
            quantityId: [
              ...this.state.countId.quantityId.slice(0, indexOfItemFound),
              { ...itemFound, quantity: itemFound.quantity - 1 },
              ...this.state.countId.quantityId.slice(indexOfItemFound + 1),
            ]
          }
        })
      }
      else {
        this.setState({
          countId: {
            ...this.state.countId,
            quantityId: [
              ...this.state.countId.quantityId.filter(element => element.id !== id)
            ]
          }
        })
      }
    }
  }

  render() {
    return (
      <div>
        <BrowserRouter>
          <Routes>
            <Route path='/store' element={<Store CountId={this.CountId} />} />
            <Route path='/cart' element={<Cart countId={this.state.countId} IncrementItem={this.IncrementItem} DecrementItem={this.DecrementItem} />} />
          </Routes>
        </BrowserRouter>
      </div>
    )
  }
}

export default App;

class Store extends Component {
  constructor(props) {
    super(props);
    this.state = {
      products: [
        {
          id: "apple"
        },
        {
          id: "lemon"
        },
        {
          id: "melon"
        }
      ]
    };
  }
  render() {
    return (
      <div>
        <Link to="/cart">Goto cart</Link>
        {this.state.products.map((p) => {
          return (
            <div id={p.id} onClick={this.props.CountId}>
              CountId - {p.id} - Click on me
            </div>
          );
        })}
      </div>
    );
  }
}


class Cart extends Component {
  render() {
    return (
      <div>
        {this.props.countId.quantityId.map((item) => {
          return (
            <div>
              <button onClick={() => this.props.IncrementItem(item.id)}>+</button>
              <div >
                {`Item is ${item.id} with quantity ${item.quantity}`}
              </div>
              <button onClick={() => this.props.DecrementItem(item.id)}>-</button>
            </div>
          );
        })}
      </div>
    );
  }
}

Upvotes: 1

Dheeraj Sharma
Dheeraj Sharma

Reputation: 244

Something is wrong here.

IncrementItem = () => {
  const deepIncrement = cloneDeep(this.state.countId.quantityId);
  this.setState({
    ...this.state.countId.quantityId,
    quantity: deepIncrement.map(i => i.id ).quantity + 1
})}

May be you can try this.

IncrementItem = (id) => {
  const itemFound = this.state.countId.quantityId.find(element => element.id ===id);
if (itemFound) {
  this.setState({
    ...this.state,
    countId: {…this.state.countId, quantityId: [{…itemFound, itemFound.quantity +1 }, …this.state.countId.quantityId.filter(element => element.id !==id)]}
})}
}

Adding a clean implementation for your reference,

App.js

import { Component } from "react";
import { Routes } from "react-router";
import Store from "./Store";
import Cart from "./Cart"
import {
BrowserRouter,
Route,
} from "react-router-dom";
import Home from "./Home";

const products = [
{
  id: 1,
  name: "Apple"
},
{
  id: 2,
  name: "Lemon"
},
{
  id: 3,
  name: "Melon"
}
]

class App extends Component {

constructor(props) {
  super(props);
  this.state = {
    products: [...products],
    cartItems: [],
  };
  this.addToCart = this.addToCart.bind(this);
  this.removeFromCart = this.removeFromCart.bind(this);
}

addToCart(id) {
  const foundItem = this.state.cartItems.find(item => item.id === id);
  if (foundItem) {
    const indexOfFoundItem = this.state.cartItems.indexOf(foundItem)
    this.setState({
      ...this.state,
      cartItems: [
        ...this.state.cartItems.slice(0, indexOfFoundItem),
        { ...foundItem, quantity: foundItem.quantity + 1 },
        ...this.state.cartItems.slice(indexOfFoundItem + 1)
      ]
    })
  } else {
    this.setState({
      ...this.state,
      cartItems: [
        ...this.state.cartItems,
        { ...this.state.products.find(product => product.id === id), quantity: 1 }
      ]
    })
  }
}

removeFromCart(id) {
  const foundItem = this.state.cartItems.find(item => item.id === id);
  if (foundItem) {
    const indexOfFoundItem = this.state.cartItems.indexOf(foundItem)
    if (foundItem.quantity > 1) {
      this.setState({
        ...this.state,
        cartItems: [
          ...this.state.cartItems.slice(0, indexOfFoundItem),
          { ...foundItem, quantity: foundItem.quantity - 1 },
          ...this.state.cartItems.slice(indexOfFoundItem + 1)
        ]
      })
    } else {
      this.setState({
        ...this.state,
        cartItems: [
          ...this.state.cartItems.filter(item => item.id !== id)
        ]
      })
    }
  }
}

render() {
  return (
    <div>
      <BrowserRouter>
        <Routes>
          <Route path='/store' element={
              <Store 
                  products={this.state.products}
                  addToCart={this.addToCart} 
              />
            } 
          />
          <Route path='/cart' element={
              <Cart 
                cartItems={this.state.cartItems} 
                addToCart={this.addToCart} 
                removeFromCart={this.removeFromCart} 
              />
            } 
          />
          <Route path='/' element={
              <Home 
                products={this.state.products} 
                cartItems={this.state.cartItems} 
                addToCart={this.addToCart} 
                removeFromCart={this.removeFromCart} 
              />
            } 
          />
        </Routes>
      </BrowserRouter>
    </div>
  )
}
}

export default App;

Home.js

import { Component } from "react";
import Cart from "./Cart";
import Store from "./Store";

export default class Home extends Component {
  render() {
    return (
      <div>
        <h1>Welcome to Store</h1>
        <hr />
        <div>
          Your Cart
          <Cart
            cartItems={this.props.cartItems}
            addToCart={this.props.addToCart}
            removeFromCart={this.props.removeFromCart}
          />
          <hr />
          Available products
          <Store
            products={this.props.products}
            addToCart={this.props.addToCart}
          />
        </div>
      </div>
    );
  }
}

Store.js

import { Component } from "react";
import { Link } from "react-router-dom";

export default class Store extends Component {
  render() {
    return (
      <div style={{maxWidth:"600px"}}>
        Product List, <Link to={'/cart'}>Click here for cart</Link>, <Link to={'/'}>Home</Link>
        <hr />
        {this.props.products.map((product, index) => {
          return (
            <div id={product.id} key={index}>
              {`${product.id}. ${product.name}`} 
              <button onClick={() => this.props.addToCart(product.id)}>Add to cart</button>
            </div>
          );
        })}
      </div>
    );
  }
}

Cart.js

import { Component } from "react";
import { Link } from "react-router-dom";

export default class Cart extends Component {
  render() {
    return (
      <div style={{maxWidth:"600px"}}>
        Choosed Items, <Link to={'/store'}>Click here for products</Link>, <Link to={'/'}>Home</Link>
        <hr />
        { this.props.cartItems.length === 0 ? 
          "Nothing is addded to the cart" : 
          this.props.cartItems.map((item, index) => {
            return (
              <div id={item.id} key={index} >
                {`Product: ${item.name}, Quantity: ${item.quantity}`}
                <button onClick={() => this.props.addToCart(item.id)}>Add one</button>
                <button onClick={() => this.props.removeFromCart(item.id)}>Remove</button>
              </div>
            );
          })
        }
      </div>
    );
  }
}

Let me know if this helps, or if you have any doubts around this. Play with it Here

Upvotes: 1

Related Questions