Miscue
Miscue

Reputation: 53

Invalid attempt to spread non-iterable instance

Would appreciate any help here. I have this piece of code which is trying to update my State whenever a form field receives a change. This worked fine in the previous version of React, but after upgrading to the latest version I'm getting the error "Invalid attempt to spread non-iterable instance".

I understand that I need to normalize my State, which I plan to. However, that will involve a significant refactor which I hoping to avoid at the moment with a quick fix.

Error

enter image description here

Code

handleMaterialTypeChange = (event, data) => {
    const material = this.state.controls.materials.materials;
    material[data.searchInput].material_type = data.value;

    this.setState(prevState => ({
        controls: {
            ...prevState.controls,
            materials: {
                ...prevState.controls.materials,
                materials: [
                    ...prevState.controls.materials.materials[data.searchInput],
                    ...material
                ]
            }
        }
    }));
};

State Example:

state = {
    controls: {
        materials: {
            value: "",
            materials: [
                {
                    material_type: "",
                    material: ""
                }
            ],
            validation: {
                required: true,
                minLength: 10
            },
            valid: false,
            touched: false
        }
    }
}

Upvotes: 5

Views: 19888

Answers (3)

trincot
trincot

Reputation: 350242

The error is caused by

[...prevState.controls.materials.materials[data.searchInput],

because you cannot spread a non-iterable object within an array literal.

If you really want to keep with the "immutable" pattern, you should not do:

const material = this.state.controls.materials.materials;
material[data.searchInput].material_type = data.value;

Without the above mutation of the state, the altered copy can be made like this:

setState(prevState => ({
    controls: {
        ...prevState.controls,
        materials: {
            ...prevState.controls.materials,
            materials: Object.assign([], {
                ...prevState.controls.materials.materials,
                [data.searchInput]: {
                    ...state.controls.materials.materials[data.searchInput],
                    material_type: data.value
                }
            })
        }
    }
}))

Upvotes: 3

adiga
adiga

Reputation: 35222

The error is coming from this line: ...prevState.controls.materials.materials[data.searchInput] because it's an object. Since you're already updating the materials array on top, no need to add another item.

You should change this part: materials: [ ...material ]

Here's some code that mimics setState

handleMaterialTypeChange = (event, data) => {
  const material = state.controls.materials.materials;
  material[data.searchInput].material_type = data.value;

  setState(prevState => ({
    controls: {
      ...prevState.controls,
      materials: {
        ...prevState.controls.materials,
        materials: [
          ...material // here
        ]
      }
    }
  }));
};

const setState = (fn) => {
  console.log(fn(state))
}

const state = {controls:{materials:{value:"",materials:[{material_type:"",material:""}],validation:{required:true,minLength:10},valid:false,touched:false}}}

handleMaterialTypeChange(null, {searchInput: 0,value: "newMaterial"})

Update: This just fixes the error but @trincot's answer explains how to do this without mutation

Upvotes: 0

BenoitVasseur
BenoitVasseur

Reputation: 782

your data structure is not easy to understand so I can not help you more than this example : https://repl.it/@Benoit_Vasseur/SO-Invalid-attempt-to-spread-non-iterable-instance

If I understood correctly you try to spread an object into an array so it does not work. You can spread an array in an array and an object in an object (type must match).

Hope that it helps :)

Upvotes: 1

Related Questions