Formally Invited: Forms in React/Ruby
Tags: ruby, react, javascript, forms
Published: 2021-09-15 03:27:26

I’ve been working long and hard on my project this week, which has involved mostly building various forms along with my database. Every website has one form or another, and I’ve built plenty in past projects, but never with this much wealth of information. So this week I thought I’d cover what I’ve learned about forms and maybe some tricks I picked up along the way.

Most of the forms I’ve built in the past were made of generic text inputs and text areas, maybe a select input, but nothing too complicated. The main purpose of my application is to match potential candidates with employers based on profiles and the job listing. That meant a lot of information would be given to make stronger matches, which also meant a lot of `useState` variables would have to be defined, and I was afraid my `handleChange` function would grow too large. Luckily I was smart enough to research a faster method that matches the name of the input with the state variable and updates with the value. Of course it’s important to assign inputs the same name as it is represented in your state.

```
    const [state, setState] = useState({
        fname: props.profile.fname,
        lname: props.profile.lname,
        city: props.profile.city
    })

    const handleChange = (event) => {
        const {name, value}  = event.target
        setState( prevState => ({
            ...prevState,
            [name] : value
        }))
    }
```

Most employee profiles I’ve seen include the type of shifts employees are willing to work like weekdays or weekends, or they are seeking a full-time or part-time position. I imagined that there would be some employees that had open availability, or at least would want to select more than one option. The question then became how to handle a post request with a parameter with multiple values as well as updating state when a checkbox is both selected and deselected. 
	
My form included four checkboxes to select for the type of position the employee was seeking, with the ability to select more than one checkbox. If a checkbox is selected, the value of the checkbox is inserted into an array, but not through any simple `.push()` function. Instead it must be handled by spreading the array being stored in state. When a checkbox is deselected, the index of the value must be found to then be spliced from the array, which will then be used to update the state.

```
    const handleJob = (e) => {
        if (e.target.checked === true) {
            setState( prevState => ({
                ...prevState,
                [e.target.name]: [...state[e.target.name], e.target.value]
            }))
        } else {
            let group = state[e.target.name]
            let deleted = group.findIndex(job => Object.keys(job)[0] == e.target.id)
            group.splice(deleted, 1)
            setState( prevState => ({
                ...prevState,
                [e.target.name]: group
            }))
        }
    }
```
If you notice, I also used the trick from above to help handle multiple sections of checkboxes, as opposed to defining a `handleChange` function for both. Not gonna lie, I was quite pleased with myself to figure that one out on my own.

Now to back up a little bit, I chose to build my database using Postgresql as I had to frequently convert my database from the Ruby default SQLite3 so I thought I’d save myself the trouble. One of the advantages of using Postgresql is that I can designate columns as an array, which I felt would be suitable to store multiple points of information in a single column. The setup is simple as well.

```
      t.text :jobType, array: true, default: []
      t.text :schedule, array: true, default: []
```
What wasn’t so simple was permitting the params to update the arrays in the database, at least because it is set up differently from the singular params usually given. Instead of `params.require(:profile).permit(:name, :schedule)` the params must be told to expect an array to be passed along by simply defining it as `params.require(:profile).permit(:name, {schedule: []})`. The trickier part comes if you’re trying to pass nested arrays, as then you have to define the keys the controller to expect, such as `params.require(:profile).permit(:name, {schedule: => :hours, :weeks})` but I was having issues with this as the parameters were being saved in the database as a distorted string that included the key and the value. Luckily I did away with nested arrays, but I’m sure they’ll come to haunt my dreams eventually.

While these were nice tricks and tips to pick up when working with forms, I still have a lot to learn and figure out. I am currently working on using nested attributes or nested forms, if that is doable in a React frontend. Based on how far I’ve gotten, I’m not sure there is a clean solution but I will keep trying until all else fails, as my params will just not permit the nested attributes. Hopefully I’ll have a working solution next week that I can try to explain. Until next time!