Update nested array of objects React

The Local state and Global state sections show examples where state data is a primitive value. Next, we will have a look into one of the most powerful features of Hookstate - the interface to a nested state of a complex object state. The interface is the same for local and global states and works equally well for both cases.

Accessing and mutating nested state

Let's consider the following example where a state value is an array of objects. It demonstrates how to dive into the nested state of the array and deeply-nested state of an element of the array. The state of an element is passed to a child component as a property. The child component gets and sets the deep nested state.

Loading...

As you can see, a state mirrors the actual properties of the corresponding state value. State of an array is an array of states. State of an object is an object of states. We can deal with a state of an object like with any other variable, including passing it as a component property, like in the example above.

We can dive to the deeply nested states of primitive values and set it, like we set the name property of a task in the

const state = useHookstate({ a: 1, b: 2 })
7 component:
const state = useHookstate({ a: 1, b: 2 })
8.

We can also set a state of an object to the entire new object. In the example above, we append new element to the state of tasks, using the

const state = useHookstate({ a: 1, b: 2 })
9 method:
state.set({ a: 2, b: 3 })
0.

state.set({ a: 2, b: 3 })1 state method

You may have noticed that a state object mixes properties from the state value object (eg.

state.set({ a: 2, b: 3 })
2 property from
state.set({ a: 2, b: 3 })
3 state) and state methods (eg.
const state = useHookstate({ a: 1, b: 2 })
9 property from interface). It is very likely that names of properties from your state objects will not collide with names of state methods. In other words, your objects are unlikely to have properties named, like
const state = useHookstate({ a: 1, b: 2 })
9,
state.set({ a: 2, b: 3 })
6,
state.set({ a: 2, b: 3 })
7, etc. However, it is more likely your objects may have properties with names like
state.set({ a: 2, b: 3 })
8 or
state.set({ a: 2, b: 3 })
9, which also exist in state methods.

If the collision happens for whatever property name, properties from state methods take priority and "hide" access to nested states via

state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
0 syntax. However, there is a
state.set({ a: 2, b: 3 })
1 state method, which allows accessing nested state by name. For example
state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
2 would give access to the same nested state as
state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
3 in the example above. So, if it ever happens that your
state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
4 object has got a property with the name, for example
state.set({ a: 2, b: 3 })
6, which collides with the corresponding state method, you would be able to obtain the nested state behind the
state.set({ a: 2, b: 3 })
6 property using nested method:
state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
7.

It is also necessary to use

state.set({ a: 2, b: 3 })
1 method instead of property access by index syntax, when a property name is unknown at compile time and comes as a variable of type
state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
9 or
const state = useHookstate({ a: 1, b: 2 })
0, for example, if you have got the state defined as the following, which allows for dynamic names of nested properties:

const dictionaryState = hookstate<Record<string, number>>({})
function NestedStateByName(props: { stateKey: string }) {
const state = useHookstate(dictionaryState)
...
}

you are required to use the

state.set({ a: 2, b: 3 })
1 method:

state.nested(props.stateKey).value

as the following would not be allowed by the typescript compiler:

state[props.stateKey].value

However, if your dynamic property name is from a set of names which are known at compile time and do not collide with state methods, you would be able to use property access by index syntax. For example:

const dictionaryState = hookstate<Record<string, number>>({})
function NestedStateByName(props: {
// note: compiler knows allowed names
stateKey: 'property1' | 'property2'
}) {
const state = useHookstate(dictionaryState)
// so access by index works, which would be equivalent to:
// state.nested(props.stateKey).value
return <>{state[props.stateKey].value}</>
}

Below, you will find more about available methods for managing nested states.

Advanced mutations for an object state

Setting new state value

Let's consider the following state:

const state = useHookstate({ a: 1, b: 2 })

One of the state methods is

state.set({ a: 2, b: 3 })
6, which is used to set the new state value.

state.set({ a: 2, b: 3 })

New state value can be also a function, which returns new value and accepts the previous one:

state.set(p => ({ a: p.a + 1, b: p.b - 1 }))

Learn more about in the API reference.

Getting names of existing properties

Let's consider the following state:

const state = useHookstate({ a: 1, b: 2 })

const state = useHookstate({ a: 1, b: 2 })
3 returns an array of names of existing properties. It is equivalent to
const state = useHookstate({ a: 1, b: 2 })
4 or
const state = useHookstate({ a: 1, b: 2 })
5.

const keys = state.keys // will be ['a', 'b'] for the above example

Learn more about in the API reference.

Updating existing property

For a given state:

const state = useHookstate({ a: 1, b: 2 })

The most efficient and recommended methods to update a nested property are the following:

state.nested(props.stateKey).value
0

These set only property

const state = useHookstate({ a: 1, b: 2 })
6, so it will rerender every component where property
const state = useHookstate({ a: 1, b: 2 })
6 is used.

Avoid the following:

There are alternative, less efficient methods, resulting in the same mutation and data state. The following sets the entire object state to the new value (although only

const state = useHookstate({ a: 1, b: 2 })
6 property is changed), so it will rerender every component where any property of the state is used.

state.nested(props.stateKey).value
1

The following sets only the property

const state = useHookstate({ a: 1, b: 2 })
6 but uses the current property value via the , which marks the property
const state = useHookstate({ a: 1, b: 2 })
6 as used by a component even if it was not used during the last rendering. In other words using nested property state in rendering or in action dispatch has the same effect: a component is rerendered on property update.

state.nested(props.stateKey).value
2

Learn more about and in the API reference.

Adding a new property

For a given state:

state.nested(props.stateKey).value
3

The recommended methods to add a new nested property are the following:

state.nested(props.stateKey).value
4

Notice the

state.set(p => ({ a: p.a + 1, b: p.b - 1 }))
4 object has got any property defined, although not every property might pass Typescript compiler check. We accessed non existing property
const keys = state.keys // will be ['a', 'b'] for the above example
2 and set it's state. It represents the fact the state of
const keys = state.keys // will be ['a', 'b'] for the above example
3 property is actually a defined state object, which can be used to set
const keys = state.keys // will be ['a', 'b'] for the above example
3 property to a new value.

It allows to add new properties to the state using the same method as is used for updating a property.

Avoid the following

as it can be potentially less efficient than the above recommended methods:

state.nested(props.stateKey).value
5

Learn more about and in the API reference.

Deleting an existing property

For a given state:

state.nested(props.stateKey).value
6

The recommended methods to delete a property are the following:

state.nested(props.stateKey).value
7

Avoid the following

as it can be potentially less efficient than the above recommended methods:

state.nested(props.stateKey).value
8

Learn more about and in the API reference.

Swapping two properties

For a given state:

state.nested(props.stateKey).value
9

The recommended method to swap properties is the following:

state[props.stateKey].value
0

Avoid the following

as it can be potentially less efficient than the above recommended method:

state[props.stateKey].value
1

Learn more about and in the API reference.

Partial updates and deletions

You may have noticed the usage of above. This does a partial update to the state and can insert, update and delete array elements all in one call:

How do you update an array of objects in ReactJS?

If you want to change some or all items of the array, you can use map() to create a new array. The function you will pass to map can decide what to do with each item, based on its data or its index (or both).

How do you update nested objects?

How to Update Nested Properties by String In JavaScript.
const setProperty = (obj, path, value) => { const [head, ... ... .
const obj = { property: { updated: false } } const updatedObj = setProperty(obj, 'property.updated', true) // The above will return: { property: { updated: false } }.
const [head, ....

How do you update an array in React state functional component?

However, with React, we need to use the method returned from useState to update the array. We simply, use the update method (In our example it's setMyArray() ) to update the state with a new array that's created by combining the old array with the new element using JavaScript' Spread operator.

How to update array of objects in JavaScript?

To update an object in a JavaScript array, you can use findIndex() method for executing each array element and updating the object values accordingly, the for loop method for iterating through an array and updating the specified value, and map() method for mapping the updated value to an object.