SolidJS
Chances are you have already heard something about SolidJS, the new reactive (as in Reactivity) UI library for front-end. Think of it as a more performant React, completely built on top of a reactive system.
You may find some similarities with Svelte as well. Actually, they share some concepts like no-virtual-dom, built-in stores for state management, and other few things.
SolidJS is a relatively new library, it reached version 1.0 around July 2021, and even if they have very nice tutorial on their site - you know, increment a counter, decrement a counter, stuff like that - I had to struggle a little bit to implement such a simple thing like submitting a form. Probably this happened because SolidJS requires a bit of a mind shift if you're coming from a React experience. But once you get familiar with the "reactivity-first" approach, everything makes much more sense.
That's why I felt the need to share the results of my initial experiments with SolidJS and forms, step by step.
Submitting a form (the naive way)
As a first attempt, I tried to add a signal (the basic unit of reactivity in SolidJS jargon) for each of the fields in my form. You can see the result in this sandbox. Source code available on Github
As you can see in the code, I had to create as many signals as my fields:
const [name, setName] = createSignal("");
const [surname, setSurname] = createSignal("");
[...]
Then I had to keep track of modifications to each field, using its own "setter" function (e.g. setName
in the example below):
<input
type="text"
id="name"
value={name()}
onChange={(e) => setName(e.currentTarget.value)}
/>
And finally I had to gather each signal with their "getter" functions (e.g. name()
, surname()
, etc.) to create an object to submit my data to some backend API service.
const dataToSubmit = {
name: name(),
surname: surname(),
address: address(),
shipping_address: sameAsAddress() ? null : shippingAddress()
};
// submit to some backend service
I was happy with this first result because, well... it worked! But I started to think that having so many createSignal
in the same simple component was kind of an antipattern. What if I had to add validation for each field? Would I have to add an additional signal for each field, just to keep track of validation errors? And what if I need to disable some fields selectively, based on the value of another field? Another createSignal
for each of those flags?
Submitting a form (the SolidJS way)
This didn't look the right approach to me. After digging a little bit deeper into SolidJS documentation I found the concept of nested reactivity, implemented via the so called store
s.
Using this approach, my form as a whole can be considered a collection of signal
s, possibly dependent on each other. Therefore, the form can be implemented as a store
.
You can check this new implementation in this sandbox. Source code available on Github
Here I created a form as a simple store, containing a single object (the set of all fields). You can create more complex stores with nested properties, if needed. SolidJS will allow you to surgically update a single nested property, without cloning the whole store.
const [form, setForm] = createStore<FormFields>({
name: "",
surname: "",
address: "",
shippingAddress: "",
sameAsAddress: false
});
Then I created a utility function to update the store based on the name of the property (so that this function can be reused for all the fields in the form).
const updateFormField = (fieldName: string) => (event: Event) => {
const inputElement = event.currentTarget as HTMLInputElement;
setForm({
[fieldName]: inputElement.value
});
};
Having a single object to update (the form) and not multiple individual fields allowed me to create such a utility function.
And finally I can submit my data collecting the needed pieces of information from the form
object.
const dataToSubmit = {
name: form.name,
surname: form.surname,
address: form.address,
shipping_address: form.shippingAddress
};
Just as an aside, having to choose between these 2 different approaches recalled me the well-known dilemma between
useState
VSuseReducer
in React.
Finally, as a bonus, splitting the logic between App.tsx
(my view) and useForm.ts
allows me to keep the UI logic separated from my business logic, following the "single responsibility" principle, which incidentally (or not?) is the first "S" in SOLID principles.
Conclusions
SolidJS seems a very promising UI library, very performant and with a very good developer experience. Being relatively young, it's not always easy to find tutorials for some common use cases, like using a form to collect data from a user and submit it to a backend API service.
I hope this post can help you to build your next real world SPA with SolidJS. In the next few weeks I would like to write another post about form validation and better UX when dealing with HTML forms.
Cover picture by Patrick Tomasso on Unsplash
Top comments (3)
What is the
type Form
for as seen in the codesandbox demo? It's been exported but never used.Hi @thojanssens ,
I think it's just a leftover from a previous experiment.
Thank you for reporting it. I've just updated the codesandbox.
thanks man! i was searching for this