Solution 1 :

You cannot mutate state, instead you can do something like this:

checkInvoiceData = (isUploaded, data) => {
  if (isUploaded) {
    this.setState({
      invoiceData: this.state.invoiceData.map(
        (invoiceItem) => {
          if (invoiceItem.number === data.savedNumber) {
            invoiceItem.details.map(
              (detail) =>
                detail.tagNumber === data.tagNumber
                  ? { ...detail, id: data.id } //copy detail and set id on copy
                  : detail //no change, return detail
            );
          }
          return invoiceItem;
        }
      ),
    });
  }
};

Solution 2 :

Perhaps try something like this:

checkInvoiceData = (isUploaded, data) => {
  // Return early
  if (!isUploaded) return

  const { invoiceData } = this.state;

  const updatedInvoices = invoiceData.map(invoiceItem => {
    if (invoiceItem.number !== data.savedNumber) return invoiceItem

    const details = invoiceItem.details.map(detail => {
      if (detail.tagNumber !== data.tagNumber) return detail

      return { ...detail, id: data.id };
    });

    return { ...invoiceItem, details };
  });

  this.setState({ invoiceData: updatedInvoices });
};

First, I would suggest returning early rather than nesting conditionals.

Second, make sure you’re not mutating state directly (eg no this.state = state).

Third, pass the part of state you want to mutate, not the whole state object, to setState.

Fourth, return a new instance of the object so the object reference updates so React can detect the change of values.

I’m not saying this is the best way to do what you want, but it should point you in a better direction.

Problem :

Following is the piece of code which is working fine, but I have one doubt regarding – const _detail = detail; code inside a map method. Here you can see that I am iterating over an array and modifying the object and then setting it to setState().

Code Block –

checkInvoiceData = (isUploaded, data) => {
    if (isUploaded) {
        const { invoiceData } = this.state;
        invoiceData.map(invoiceItem => {
            if (invoiceItem.number === data.savedNumber) {
                invoiceItem.details.map(detail => {
                    const _detail = detail;
                    if (_detail.tagNumber === data.tagNumber) {
                        _detail.id = data.id;
                    }
                    return _detail;
                });
            }
            return invoiceItem;
        });
        state.invoiceData = invoiceData;
    }
    this.setState(state);
};

Is this approach ok in React world or I should do something like –

const modifiedInvoiceData = invoiceData.map(invoiceItem => {
  ......
   code
  ......
})

this.setState({invoiceData: modifiedInvoiceData});

What is the pros and cons of each and which scenario do I need to keep in mind while taking either of one approach ?

Comments

Comment posted by Brian Thompson

A rule of thumb: Anytime you notice you are not using the return value of

Comment posted by ilkerkaran

state.invoiceData = invoiceData;

Comment posted by TJBlackman

Convention in React land is to make new copies of an array or object or really any data. This is especially important when you start using state management libs like Redux.

Comment posted by Dave Newton

@ilkerkaran That says what, but not why.

By