DEV Community

Cover image for How to submit a form with SolidJS
Christian Sarnataro
Christian Sarnataro

Posted on • Updated on • Originally published at dev.to

How to submit a form with SolidJS

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("");
[...]
Enter fullscreen mode Exit fullscreen mode

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)}
/>
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 stores.

Using this approach, my form as a whole can be considered a collection of signals, 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
});

Enter fullscreen mode Exit fullscreen mode

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
  });
};
Enter fullscreen mode Exit fullscreen mode

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
};
Enter fullscreen mode Exit fullscreen mode

Just as an aside, having to choose between these 2 different approaches recalled me the well-known dilemma between useState VS useReducer 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)

Collapse
 
thojanssens profile image
thojanssens

What is the type Form for as seen in the codesandbox demo? It's been exported but never used.

Collapse
 
csarnataro profile image
Christian Sarnataro

Hi @thojanssens ,
I think it's just a leftover from a previous experiment.

Thank you for reporting it. I've just updated the codesandbox.

Collapse
 
devpawankr profile image
Pawan Kumar

thanks man! i was searching for this