Does ReScript support a full wrapper of jQuery?

That is, are all jQuery functionality available easily when using ReScript? I’m working on a legacy application and might need to interact some with older HTML and JS.

JQuery api doesn’t have anything that would be impossible to interop with. Usually I could have problems when a lib uses generators, proxies, or require doing some dirty mutations with unstructured objects. That’s not the case for JQuery

1 Like

Thanks for your answer! Is there a link with examples?

You can create a JQuery.res file and add in bindings as you come across features you use.

For example

type t

@module external jQuery: string => t = "jquery"

@send external append: (t, string) => t = "append"
@send external appendElement: (t, t) => t = "append"
@send external appendElementMany: (t, t[]) => t = "append"
@send external addClass: (t, string) => t = "addClass"
@send external removeClass: (t, string) => t = "removeClass"
@send external text: (t, string) => t = "text"

And then to use would look like

open JQuery

jQuery("#item")
  ->append("<p>")
  ->addClass("some-class")
  ->text("Hello World!")
3 Likes

That’s great!, buuut no one did that already? :thinking: Maybe jQuery is not commonly used in ReScript projects?

JQuery isn’t really common in general outside of legacy projects. You can do most of jQuery’s useful features natively in the browser now, and people tend to prefer component based development these days

4 Likes

After trying both bs-fetch and react-query without success, maybe you can help me with writing the types for jQuery.get() and .post() for JSON…?

Edit, actually got both bs-fetch and react-query to work now, by tinkering with -> and |>. Examples in source are not correct.

So for me personally, I’ve found that most bindings are easy enough to just do yourself real quick, and as the only consumer you can simplify the api since you don’t need to cover “every case”

For instance, for fetch, I just created a loosely typed fetch binding and then added on helper methods for get, post, etc. It looks something like this:

module Fetch = {
  exception Unexpected_Error
  exception Not_Found

  type response<'a> = {status: int}

  let stringify = obj => Js.Json.stringifyAny(obj)->O.orElse("")

  @send external json: response<'a> => 'a = "json"
  @scope("window") @val external fetch: (string, {..}) => Promise.t<response<'a>> = "fetch"

  let mapStatus = response =>
    switch response.status {
      | 200 => json(response)
      | 404 => raise(Not_Found)
      | _ => raise(Unexpected_Error)
    }

  let get = url => 
    fetch(url, {"method": "GET"})
    -> Promise.map(mapStatus)

  let post = (url, body) =>
    fetch(url, {
      "method": "POST",
      "body": stringify(body),
      "headers": {
        "Content-Type": "application/json",
      },
    })-> Promise.map(mapStatus)
}

And then I have a module for my actual API calls

module Game = {
  type responseWithId = {gameId: string}
  external unsafeCastGameId: Promise.t<'a> => Promise.t<responseWithId> = "%identity"

  let create = (userId: string, courseId: string) =>
    Fetch.post(endpoint("/games"), {
      "userId": userId,
      "courseId": courseId,
    },)->unsafeCastGameId

  let join = (userId: string, gameId: string) =>
    Fetch.post(endpoint(`/games/${gameId}/join`), {"userId": userId})->unsafeCastGameId

  let fetch = gameId => 
    Fetch.get(endpoint(`/games/${gameId}`))->unsafeCastGame

  let scores = gameId =>
    Fetch.get(endpoint(`/games/${gameId}/scores`))->unsafeCastScores

  let addScore = (~gameId: string, ~userId: string, ~label: string) => {
    Fetch.post(endpoint(`/games/${gameId}/scores`), {
      "userId": userId,
      "label": label,
    })
  }
}

I did something similar for react-query, and in the end my query looks like this

let useCurrentGame = () => {
  let (roomId, _) = useRoomId()
  let roomId = O.orElse(roomId, "DOESNT_EXIST")
  
  RQ.useQuery(
    RQ.key(["games", roomId]), 
    () => Api.Game.fetch(roomId)
  )
}
1 Like