0

Here is a reducer I created used in a custom hook for a form.

const initialStateObject = (nestedKey) => ({
  value: '',
  touched: false,
  hasError: false,
  error: '',
  nestedKey
})

export const INITIAL_STATE = {
  name: initialStateObject(),  
  contactNumber: initialStateObject(),  
  address: {
    line1: initialStateObject('address'),
    postCode: initialStateObject('address'),
    country: initialStateObject('address'),
    city: initialStateObject('address'),        
    nested: true
  },
  adminUser: {        
    email: initialStateObject('adminUser'),
    nested: true
  },
  isFormValid: false,
}

export const formReducer = (state, action) => {
  const { data, type } = action
  const { name, value, hasError, error, touched, isFormValid, nestedKey } = data
  switch (type) {
    case 'updated_input':
      if (!nestedKey) {
        return {
          ...state,
          [name]: { ...state[name], value, hasError, error, touched, nestedKey },
          isFormValid,
        }
      }
      return {
        ...state,

        [nestedKey]: {
          ...state[nestedKey],
          [name]: { ...state[name], value, hasError, error, touched, nestedKey },
        },
        isFormValid,
      }
    case 'save_form':
        return {
            data
        }
    default:
      return state
  }
}

I don't know when I need to create an "action". The main action is updated_input. However, I added the save_form action. When the form is "saved" I need to reconstruct the "state" into an object that the backend accepts (I only need the value property, the backend accepts key/value pairs without properties such as error, hasError, touched, nestedKey, here is the save_form function:

const getFormPayload = (formInputs) => {    
    return Object.keys(formInputs).reduce((prev, next) => {         
        const obj = formInputs[next]                
        if(!obj.nested && obj.value) {
            prev[next] = obj.value
            return prev
        }
        if(obj.nested) {
            prev[next] = Object.keys(obj).reduce( (prev, next) => {
                const nestedObj = obj[next]
                if(nestedObj.value) prev[next] = nestedObj.value
                return prev
            }, {})
            return prev
        }
        return prev;        
    }, {})
}

const handleSave = (e: React.FormEvent<HTMLFormElement>) => {    
    e.preventDefault()
    
    if(formInputs.isFormValid) {
        const body = getFormPayload(formInputs)
        dispatch({
            type: 'save_form', 
            data: body
        })        
        saveForm(body)
    }    
  }

I don't know if I need the save_form action. First, this feels like a code smell that I'm setting the state to an object with a new structure. It goes from

{
  key: {
    value: '',
    touched: false,
    hasError: false,
    error: '',
    nestedKey
  }
}

To:

{ key: "some string value or nested object" }

In the event the save action fails, if I replace the state with properties required by the backend, I can't reference errors. This also raises the question if I need an action such as: save_form_error (when the server responds with an error) and save_form_in_progress (when the form is in between submission and a response)

I am struggling with the concepts of useReducer, I can only find examples of a counter. I could benefit in making progress on this question by reviewing more examples on when to make a new action and reason for making new action. If anybody has insight or can link to code examples that isn't a counter it would be extremely useful to study.

HelloWorld
  • 10,529
  • 10
  • 31
  • 50
  • I agree with your instincts that it’s not a good idea to change the structure of the state. You can think of your getFormPayload function as a “selector” in redux terms. It takes the current state and derives a value which is the API payload. That derived value does not need to become part of the state. – Linda Paiste Feb 20 '23 at 16:20
  • You should definitely not change the redux state from your form setup to a save setup. Who will handle the saving ? Are you using sagas ? (you also have an error in your reducer when there is a `nestedKey` it should be `[name]: { ...state[nestedKey][name], value, ...`) – Gabriele Petrioli Feb 20 '23 at 16:25

0 Answers0