3

I have an object defined like this:

import React, { Component } from 'react';
import './App.css';

class App extends Component {

  constructor() {
    super();
    this.state = {
    lists: ["Dogs", "Cats"],
    items: {Dogs:[], Cats:[]}
    };
  }

  handleAddItem(item) {
    console.log(this.props.idName);
    console.log(item);
  }

I have the variable

console.log(this.props.idName)// output: Dogs
console.log(item);// output {name: "lofi"}  

I don't know how to update the object items{} to make it becоme like this:

items{Dogs:[{name: "lofi"}], Cats:[]}
Al.G.
  • 4,327
  • 6
  • 31
  • 56
E.Mohammed
  • 163
  • 1
  • 4
  • 13

3 Answers3

1

To update a nested Array substate, you can use the spread operator to append elements

handleAddItem = item => {
  this.setState((prevState, props) => ({
    items: {
      ...prevState.items,
      [props.idName]: [
        ...prevState.items[props.idName],
        item
      ]
    }
  }))
}
lipp
  • 5,586
  • 1
  • 21
  • 32
  • 1
    It is better to use setState with callback function in such case: https://reactjs.org/docs/react-component.html#setstate – amaj Jun 04 '18 at 09:57
  • 2
    From React docs - `this.props` and `this.state` may be updated asynchronously, you should not rely on their values for calculating the next state. – amaj Jun 04 '18 at 10:04
  • yeah. for things like incrementing etc I see the point, how would your solution look like? I don't see a more "atomic" way to do it. – lipp Jun 04 '18 at 10:06
  • I get this error App.js:5 Uncaught TypeError: Cannot convert undefined or null to object – E.Mohammed Jun 04 '18 at 10:09
  • handleAddItem is missing the "this" context. Write the declaration `handleAddItem = item => {}` as I did, or bind the function correctly to this. – lipp Jun 04 '18 at 11:03
  • 1
    @amaj Thanks for pointing this out! Good watch! I updated the code accordingly. You example is not using the props from the updater function, so you may fix your code as well :) – lipp Jun 04 '18 at 11:04
1

What about something like that

handleAddItem(item) {
    this.setState((s, p) => ({
        items: {
            ...s.items,
            [p.idName]: s.items[p.idName].concat([item])
        }
    }))
}

Few comments:

  • setState can take function, as parameter you got old state value
  • [idName]: value dynamically updates prop idName
amaj
  • 270
  • 1
  • 2
  • 8
0

You can do something like

import React, { Component } from 'react';
import './App.css';

class App extends Component {

  constructor() {
    super();
    this.state = {
    lists: ["Dogs", "Cats"],
    items: {Dogs:[], Cats:[]}
    };
  }

  handleAddItem(item) {
    console.log(this.props.idName);
    console.log(item);
    let oldItems = this.state.items;
    oldItems[this.props.idName].push(item);
    // Update new state for items
    this.setState({
      items: {...this.state.items}
    })

  }
agenthunt
  • 8,450
  • 3
  • 30
  • 26
  • Am updating the previous state variable to be make it ready to update for the next state. You can see spread operator in the this.setState call which updates it how react wants. Let me update the answer to make it clear. – agenthunt Jun 04 '18 at 09:54
  • You can pass function in setState, where you have access to oldState and you don't have to save previous state value, check docs: reactjs.org/docs/react-component.html#setstate – amaj Jun 04 '18 at 10:06
  • This may corrupt data! The setState call would overwrite the other category! – lipp Jun 04 '18 at 10:07
  • @lipp. How so ? – agenthunt Jun 04 '18 at 10:15
  • @agent_hunt `this.state.items` gets replaced entirely. If `this.state.items.Cats = [{name: 'foo'}]` and `idName = 'Dogs'`, the 'Cats' get removed. That said: I just noticed, that your are missing "one level". It is e.g. this.state.items.Cats = [{name: 'foo'}] ... and not this.state.items = [{name: 'foo'}] – lipp Jun 04 '18 at 10:58
  • @lipp ` let oldItems = this.state.items; oldItems[this.props.idName].push(item);` will add items only to the category. So for ex: if idName = Dogs . oldItems['Dogs'].push(item) . Am not touching anything related to Cats. I dont see how this.setState will corrupt data . – agenthunt Jun 04 '18 at 11:06
  • @agent_hunt this.state.items = {Cats: [], Dogs: []}. items is an object with two fields (as from the constructor). You are assigning an array to items => this.state.items = []. – lipp Jun 04 '18 at 11:08