Working with results from DOM API (like file arrays and objects)

reasonreact

#1

Hi,

im trying to convert an existing react app to reasonml and react-reason. Here i have some problems with the types of arrays and object which come directly from the DOM files input via ReactDOMRe.domElementToObj(ReactEventRe.Form.target(_event))##files .

It works, but the Object created with “fileToJs” is just an Object with all values undefined. Js logging Belt.Array.getExn(_state.files, 0) gives me the correct to object with all key values pairs filled correctly.

Here is a Gist of my current code: react-reason example

Maybe someone could explain how to correctly type the result from DOM API, so it can be used in the bs-axios request?


Using Reason Apollo and the File API
#2

From what I can read here, you don’t need to convert state.files to JS, because what you extract using ReactDOMRe.domElementToObj(ReactEventRe.Form.target(_event))##files already is a JS object (and not a record) you can pass to axios.

If you want to type file as JS object, you can use the following type definition:

type file = {
  .
  "lastModified": int,
  "lastModifiedDate": string,
  "name": string,
  "size": int,
  "type_": string,
  "webkitRelativePath": Js.Nullable.t(string),
};

#3

thanks! Thats what i thought too, but when i use it like that, axios creates a post request with no payload/body at all.

after a lot of fiddling around i got it working like this:
Updated Example

using the following as the file type:

[@bs.deriving abstract]
type file = {
  .
  "lastModified": int,
  "lastModifiedDate": string,
  "name": string,
  "size": int,
  "type_": string,
  "webkitRelativePath": string,
};

(don’t know if abstract is really needed here)

then Encoding to JSON with:

module Encode = {
  let toJson = file =>
    Json.Encode.(
      object_([
        ("lastModified", file##lastModified |> int),
        ("lastModifiedDate", file##lastModifiedDate |> string),
        ("name", file##name |> string),
        ("size", file##size |> int),
        ("type", file##type_ |> string),
        ("webkitRelativePath", file##webkitRelativePath |> string),
      ])
    );
};

Im pretty sure that this is not the correct way, but its the only way i got it working. But i would love to get it working with bs-axios.


#4

The [@bs.deriving abstract] isn’t necessary in that case. My guess is that the the kept object might be emptied by the browser. Have you tried creating a copy of it (using Js.Obj.assign(Js.Obj.empty(), file) for instance?


#5

Creating a copy of it like that, gives me an empty Object when i Js.log it :open_mouth:


#6

The properties might not be enumerable. You might need to do:

let file = {
  "lastModified": eventFile##lastModified,
  "lastModifiedDate": eventFile##lastModifiedDate,
  "name": eventFile##name,
  "size": eventFile##size,
  "type_": eventFile##type_,
  "webkitRelativePath": eventFile##webkitRelativePath,
};

#7

thank you so much :slight_smile:

This did the trick, for using bs-axios:

      let selectedFile = Belt.Array.getExn(_state.files, 0);
      let payloadFile = {
        "lastModified": selectedFile##lastModified,
        "lastModifiedDate": selectedFile##lastModifiedDate,
        "name": selectedFile##name,
        "size": selectedFile##size,
        "type_": selectedFile##type_,
        "webkitRelativePath": selectedFile##webkitRelativePath,
      };
      Js.Promise.(
        Axios.postData("/auth", payloadFile)
        |> then_(response => resolve(Js.log(response##data)))
        |> catch(error => resolve(Js.log(error)))
      );

Could have thought about it, since it worked with Encoding it to JSON via "lastModified": file##lastModified .

Actually Uploading a file will be the next challange :smiley: