lucas24007
lucas24007

Reputation: 153

React - update state array

Context

Hi,

I'm creating a form to create virtual machine. Informations are gathered across multiple pages (components) and centralized in top level component.

It's state object looks like this :

const [vmProp, setVmProp] = useState({
    name: "",
    image_id: "",
    image_name: "",
    volumesList: [],
    RAM: "",
    vcpu: "",
    autostart: true,
    });

Problem

I want to be able to add/remove a volume to the volumes List and I want volumesList to looks like this :

[
    {
      name: "main",
      size: 1024
    },
    {
      name: "backup",
      size: 2048
    },
    {
      name: "export",
      size: 2048
    }
]

What I've tried

For now I've only tried to add a volume.

1 : working but does not produce what I want

const handleAddVolume = (obj) => {
    setVmProp({ ...vmProp,
        volumesList: {
            ...vmProp.volumesList,
            [obj.name]: {
                size: obj.size,
            },
        } });
};

It's working but the output is :

[
    name: {
      size: 1024
    }
]

2 : should be working but it's not

const handleAddVolume = (obj) => {
    setVmProp({ ...vmProp,
        volumesList: {
            ...vmProp.volumesList,
            {
                name: obj.name,
                size: obj.size,
            },
        } });
};

output :

[
    name: "main",
    size: 1024

]

Do you have any ideas on how to handle such state object ? Thanks

Upvotes: 0

Views: 103

Answers (4)

lucas24007
lucas24007

Reputation: 153

As I needed to be able to add / remove / replace an object in my array I decided to create it's own state as following :


const [volumesList, setVolumesList] = useState([]);

const handleAddVolume = (obj) => {
    setVolumesList((oldList) => [...oldList, obj]);
};

const handleRemoveVolume = (obj) => {
    setVolumesList((oldList) => oldList.filter((item) => item.name !== obj.name));
};

const handleEditVolume = (obj) => {
    setVolumesList(
        volumesList.map((volume) =>
            (volume.name === obj.name
                ? { ...volume, ...obj }
                : volume),
        ),
    );
};

Thanks for all your answers !

Upvotes: 0

Osama Sayed
Osama Sayed

Reputation: 2023

Here is a working example:

const Example = (props) => {
  const inputRef = createRef();
  const [vmProp, setVmProp] = useState({
    name: "",
    image_id: "",
    image_name: "",
    volumesList: [],
    RAM: "",
    vcpu: "",
    autostart: true,
  });

  const { volumesList } = vmProp;

  const handleAddVolume = (e) => {
    e.preventDefault();
    const input = inputRef.current.value;
    setVmProp((prevVmProp) => {
      const newVmProp = { ...prevVmProp };
      newVmProp.volumesList.push({
        name: input,
        size: 1024,
      });
      return newVmProp;
    });
    // reset the input
    inputRef.current.value = "";
  };

  return (
    <>
      <form>
        <input ref={inputRef} type="text" />
        <button onClick={handleAddVolume}>Add volume</button>
      </form>
      {volumesList.map((volume) => (
        <div key={volume.name}>{`${volume.name} - ${volume.size}`}</div>
      ))}
    </>
  );
};

export default Example;

it's just a proof of concept. Basically, setVmProp accepts a function that gets the up-to-date state value as the update is asynchornus and returns the new value of the state. so using ES6's destructuring function, I make a copy of the latest value of vmProp called newVmProp , then push a new object to newVmProp.volumesList then return newVmProp that contains the newly added volume + everything else. The value of the name I get from an input field. Again this is just a proof of concept.

Upvotes: 0

Sarun UK
Sarun UK

Reputation: 6736

volumesList shouldn't be object, make it as an array inside handleAddVolume function.

Try the below approach,

const handleAddVolume = (obj) => {
    setVmProp({ ...vmProp,
        volumesList: [
            ...vmProp.volumesList,
            {
                name: obj.name,
                size: obj.size,
            }
        ] });
};

Upvotes: 1

AKX
AKX

Reputation: 168824

You'll have a better time if you don't need to nest/unnest the volumes list from the other state. (Even more idiomatic would be to have a state atom for each of these values, but for simple values such as booleans and strings, this will do.)

const [vmProps, setVmProps] = useState({
  name: "",
  image_id: "",
  image_name: "",
  RAM: "",
  vcpu: "",
  autostart: true,
});
const [volumesList, setVolumesList] = useState([]);

Upvotes: 1

Related Questions