0

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

1
  • 1
    Make the volumesList its own state atom, you'll have a much better time. (Preferably make all of the various bits their own atoms.) Commented Sep 28, 2020 at 13:18

4 Answers 4

1

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([]);
Sign up to request clarification or add additional context in comments.

Comments

1

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,
            }
        ] });
};

Comments

0

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.

Comments

0

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 !

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.