Using FormData in rescript react

Hi, I have a Rescript react component which contains a form. I am trying to get the FormData when the form is submitted, but I am a bit confused about how to do that.

Here is my react component:

type todo = {text: string, id: string}

@react.component
let make = () => {
  let (todos, updateTodos) = React.useState(_ => [{text: "First todo", id: "abc123"}])

  let addTodo = evt => {
    ReactEvent.Form.preventDefault(evt)
    let formElem = ReactEvent.Form.target(evt)->Webapi.Dom.HtmlFormElement.ofElement
    let todoInput = formElem->data->get("todoInput")
    let newTodo: todo = {text: todoInput, id: Belt.Float.toString(Js.Math.random())}
    updateTodos(prev => Js.Array2.concat([newTodo], prev))
  }

  <>
    <h1> {React.string("Welcome to the TODO App")} </h1>
    <form onSubmit={addTodo}>
      <input type_="text" name="todoInput" />
      <button> {React.string("Add Todo")} </button>
    </form>
    <h2> {React.string("Todos:")} </h2>
    <div> {React.array(todos->Belt.Array.map(todo => {<TodoItem todo key={todo.id} />}))} </div>
  </>
}

I am trying to use the rescript-webapi library to get the FormData, but I am having trouble figuring out how to use it properly (or perhaps its an issue with using it with ReactEvent?).

I get the following error:

This has type: {..}
Somewhere wanted:
  Dom.element (defined as
    Dom.eventTarget_like<Dom._node<Dom._element<Dom._baseClass>>>)

on this line: let formElem = ReactEvent.Form.target(evt)->Webapi.Dom.HtmlFormElement.ofElement

The bindings and tests for FormData:

I can’t answer your question, cause I haven’t used the webapi yet, but why do you convert the form to a Formdata object and than get the values out of the Formdata? Why don’t you get the values directly?

Could you give an example?

Sure. Here is an updated addTodo function:

let addTodo = evt => {
  ReactEvent.Form.preventDefault(evt)

  let todoInput = ReactEvent.Form.target(evt)["todoInput"]["value"]
  // ...
}

But this is only one way to hande forms. You could also use controlled components where the value is stored in a state or in a ref or whatever.

Oh sure, that would work, but to be a bit “nit picky” about it, that’s not really FormData, thats just the elements value.

I was hoping there was a way in Rescript to use the FormData api like this: FormData: FormData() constructor - Web APIs | MDN

You’re right, but if you don’t use the formData (you don’t send it to a server for example), I don’t see a benefit?!

I guess the main advantage is if you have a more complex form, you can easily just get all the data from the form using formData.entries method: FormData: entries() method - Web APIs | MDN

I can definately use a controlled components like you mentioned, but when the form gets more complicated it means a little more wiring up. Not a huge issue, its just the FormData approach to getting all the input values seems a little cleaner.

Okay, so I added a function to create the formData

@new external toFormData: {..} => Webapi.FormData.t = "FormData"

and changed the addTodo to this:

let addTodo = evt => {
  ReactEvent.Form.preventDefault(evt)
  let formData = ReactEvent.Form.target(evt)->toFormData
  let todoInput = formElem->Webapi.FormData.get("todoInput")

  Js.log(formElem)
  Js.log(todoInput)
}

I didn’t find a built in function for this. But I also have some trouble with my IDE since I updated it yesterday :frowning:

Thanks for the help. With your example, I was able to make it work with FormData.entries too:

    let formData = ReactEvent.Form.target(evt)->toFormData
    let entries = formData->Webapi.FormData.entries

    Webapi.Iterator.forEach(~f=(. item) => {
      Js.log(item)
    }, entries)
1 Like