You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As I worked on a project I realized how complex edit forms can be, specially when the actions taken by the user can alter multiple tables of a database. I realized that I was making the backend code more complicated, thus making it harder to manage and more prone to errors.
I was looking for a way to simplify the process of creating such forms. I came up with ucr.
The idea is to have a function that returns an update, create and remove object in the following format:
In order to have such format each <input/> will handle one column in the database and update an UcrObject. The key of an UcrObject is the name of the column in the database. The value of an UcrObject is an object with two keys: action and value. The action key is used to determine what action must be taken by the backend. The value key is the value of the column in the database.
The form state is then parsed by the getUCR() function to return an object which only contains the data that must be sent to the backend.
I created a todo list example of how this could work with react-hook-form. This todo list has todos and tasks. Each todo has multiple tasks. The user can create, update and delete todos and tasks. Here's a live version.
// the `todo` and `tasks` objects come from your formconstpayload=getUCR({todos: [todo],
tasks,});
This returns the following object:
{
"update": {
"todos": [
{
"todoId": "12",
"name": "Shopping",
"description": "These are the things that I must buy."
}
],
"tasks": [
{
"taskId": "3",
"name": "Eggs",
"completed": true
}
]
},
"create": {
"todos": [],
"tasks": [
{
"name": "Potatoes",
"completed": false
}
]
},
"remove": {
"todos": [],
"tasks": [4]
}
}
Downsides
You must create more complex <input/> elements which handle the UcrObject state.
Upsides
Only the dirty values are sent to the backend. This is important for performance reasons. If I send the entire state of the form to the backend, I may be updating columns that have not been modified.
The backend is simplified. Functions should only perform one action. In this manner I could have create, remove and update functions which can even be shared in different forms containing the same tables.
This is an example of how the backend code could look like:
exportasyncfunctionhandleForm(formData: unknown){const{ create, remove, update }=handleFormSchema.parse(formData);// You may insert auth logic hereawaitremoveTasks(remove.tasks);awaitupdateTasks(update.tasks);awaitcreateTasks(create.tasks);awaitupdateTodos(update.todos);}
Help Needed
I’m struggling to abstract the logic needed for the <input/> elements with react-hook-form.
This input sets the action key to UPDATE if the value is different from the default value (fetched from the database). If the value is the same as the default value, then the action key is set to an empty string. Otherwise, the action key is set to UPDATE.
<inputclassName="ring-1"{...register("todo.name.value",{onChange: ({ target })=>{const{ value }=targetasHTMLInputElement;setValue(`todo.name.value`,value);if(value===defaultValues?.todo?.name?.value){setValue(`todo.name.action`,"");return;}setValue(`todo.name.action`,`UPDATE`);},})}/>
This in an example for an input inside an array:
<inputclassName="ring-1"{...register(`tasks.${index}.name.value`,{onChange: ({ target })=>{const{ value }=targetasHTMLInputElement;setValue(`tasks.${index}.name.value`,value);if(value===defaultValues?.tasks?.[index]?.name?.value){setValue(`tasks.${index}.name.action`,"");return;}setValue(`tasks.${index}.name.action`,`UPDATE`);},})}/>
The delete buttons remove the task from the tasks array if the taskId is empty. If the taskId is not empty, then the name column is updated with the REMOVE action.
The getUCR() function detects if any of the action keys is REMOVE and adds the taskId to the remove object.
There is a discussion on how to use dirty values, but I find that it is not enough.
I would still need to write functions to add the ids of the items to be updated or removed, and the value of the input is not directly available.
I also much rather move the complexity to the Front-End.
Conclusion
My vision is to have a package that can be used with react-hook-form to handle the UcrObject state. The missing piece is how to abstract the logic needed for the <input/> elements. I would love to hear your thoughts on how to do this. It could be a custom hook or a component, a react-hook-form plugin, I'm not sure. Thanks!
Note: I use the word remove instead of delete because delete is a reserved word in JavaScript.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
As I worked on a project I realized how complex edit forms can be, specially when the actions taken by the user can alter multiple tables of a database. I realized that I was making the backend code more complicated, thus making it harder to manage and more prone to errors.
I was looking for a way to simplify the process of creating such forms. I came up with ucr.
The idea is to have a function that returns an
update
,create
andremove
object in the following format:In order to have such format each
<input/>
will handle one column in the database and update anUcrObject
. The key of anUcrObject
is the name of the column in the database. The value of anUcrObject
is an object with two keys:action
andvalue
. Theaction
key is used to determine what action must be taken by the backend. Thevalue
key is the value of the column in the database.The form state is then parsed by the
getUCR()
function to return an object which only contains the data that must be sent to the backend.I created a todo list example of how this could work with
react-hook-form
. This todo list has todos and tasks. Each todo has multiple tasks. The user can create, update and delete todos and tasks. Here's a live version.This returns the following object:
Downsides
You must create more complex
<input/>
elements which handle theUcrObject
state.Upsides
Only the
dirty
values are sent to the backend. This is important for performance reasons. If I send the entire state of the form to the backend, I may be updating columns that have not been modified.The backend is simplified. Functions should only perform one action. In this manner I could have
create
,remove
andupdate
functions which can even be shared in different forms containing the same tables.This is an example of how the backend code could look like:
Help Needed
I’m struggling to abstract the logic needed for the
<input/>
elements withreact-hook-form
.This input sets the
action
key toUPDATE
if the value is different from the default value (fetched from the database). If the value is the same as the default value, then theaction
key is set to an empty string. Otherwise, theaction
key is set toUPDATE
.This in an example for an input inside an array:
The delete buttons remove the task from the
tasks
array if thetaskId
is empty. If thetaskId
is not empty, then thename
column is updated with theREMOVE
action.The
getUCR()
function detects if any of theaction
keys isREMOVE
and adds thetaskId
to theremove
object.How do you suggest I could abstract this logic?
Why not not use dirty values?
There is a discussion on how to use dirty values, but I find that it is not enough.
I would still need to write functions to add the ids of the items to be updated or removed, and the value of the input is not directly available.
I also much rather move the complexity to the Front-End.
Conclusion
My vision is to have a package that can be used with
react-hook-form
to handle theUcrObject
state. The missing piece is how to abstract the logic needed for the<input/>
elements. I would love to hear your thoughts on how to do this. It could be a custom hook or a component, areact-hook-form
plugin, I'm not sure. Thanks!Note: I use the word
remove
instead ofdelete
becausedelete
is a reserved word in JavaScript.References
react-hook-form
.ucr-form
.react-hook-form
anddrizzle
.Beta Was this translation helpful? Give feedback.
All reactions