Adrian
Adrian

Reputation: 75

How to push an object into an array with React Hooks

I am trying to push an obj into an array located in a nested object. What would be the best approach because I want it to render the component as well with the new obj.

What I want to achieve with this is for the user to be able to create new categories for the tasks

const [formElements, setFormElements] = useState({
    task: {
        type: 'text',
        value: 'text',
        className: 'task-input'
    },
    categories: {
        type: 'select',
        value: 'select',
        className: 'categories-input',
        cat: [
            {value: 'money', displayValue: 'Money'},
            {value: 'health', displayValue: 'Health'},
            {value: 'daily', displayValue: 'Daily'},
            {value: 'food', displayValue: 'Food'}
        ]
 
        
    }
})

   const buttonAddCategoryHandler = (e) => {
        e.preventDefault()
        setFormElements(prevState => {
            ???

        })
    }

I know this might be a bad question but I have a problem understanding how to update the state in a situation like this. Or this way of having a state is bad from the start?

Upvotes: 2

Views: 664

Answers (4)

Airis
Airis

Reputation: 66

There are several ways to achieve this, but try using spread operator:

setFormElements((prevState) => {
  return({
    ...prevState,
    categories: {
      ...prevState.categories,
      cat: [
        [...prevState.categories.cat, 
        {value: '123', displayValue: 'abc'}]
      ]
    }
  })
})  

Upvotes: 1

maxwell
maxwell

Reputation: 2381

I would take a look at using the slightly more advanced hook; useReducer. Check out this note from the react docs:

Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax. Another option is useReducer, which is more suited for managing state objects that contain multiple sub-values.

Using useReducer, your example above might look something like:

const [state, dispatch] = useReducer((state, action) => {
    switch(action.type){
        case "UPDATE_CAT":
            return { 
                ...state, 
                categories: {
                    ...state.categories,
                    cat: [...state.cat, action.value]
                }
            }
        default:
            return state
    }
})

   const buttonAddCategoryHandler = (e) => {
        e.preventDefault()
        setFormElements(prevState => {
            dispatch({ type: "UPDATE_CAT", value: { value: "vitamins", displayValue: "Vitamins" } })
        })
    }

Upvotes: 1

Nicolae Maties
Nicolae Maties

Reputation: 2655

const [formElements, setFormElements] = useState({
    task: {
        type: 'text',
        value: 'text',
        className: 'task-input'
    },
    categories: {
        type: 'select',
        value: 'select',
        className: 'categories-input',
        cat: [
            {value: 'money', displayValue: 'Money'},
            {value: 'health', displayValue: 'Health'},
            {value: 'daily', displayValue: 'Daily'},
            {value: 'food', displayValue: 'Food'}
        ]
 
        
    }
});

const updatedCategories = (categories) => {
   const catUpdated = categories.cat;
   catUpdated.concat({value: 'new', displayValue: 'NEW'});
   return {
      ...categories,
      cat: catUpdated
   }
}

   const buttonAddCategoryHandler = (e) => {
        e.preventDefault()
        const updatedState = {
          ...formElements,
          categories: updatedCategories(formElements.categories)
        }
        setFormElements(updatedState);
    }

You can use your old state and update it before making another setState.

Upvotes: 0

Bjorne
Bjorne

Reputation: 1484

   const buttonAddCategoryHandler = (e) => {
        e.preventDefault()
        const newArray = [...formElements.categories.cat, {value: 'newVal', displayValue: 'Friendly Value'}]
        setFormElements({
          ...formElements,
          categories: {
             ...formElements.categories,
             cat: newArray
             
          }

        })
    }

Upvotes: 2

Related Questions