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.