Skip to content

Instantly share code, notes, and snippets.

@reyronald
Forked from chenglou/FetchedDogPictures.re
Created May 19, 2020 08:23
Show Gist options
  • Select an option

  • Save reyronald/9620bfb37b367ada60f3acb84a31c861 to your computer and use it in GitHub Desktop.

Select an option

Save reyronald/9620bfb37b367ada60f3acb84a31c861 to your computer and use it in GitHub Desktop.

Revisions

  1. @chenglou chenglou revised this gist Mar 12, 2020. 1 changed file with 10 additions and 2 deletions.
    12 changes: 10 additions & 2 deletions FetchedDogPictures.re
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,12 @@
    // hey you two! Ignore these externals for now. They're just for illustration
    // This is a proper alternative to
    // https://github.com/BuckleScript/bucklescript/blob/b9508105b1a35537bdea9a1fabd10f6c65f776b4/jscomp/bsb/templates/react-hooks/src/FetchedDogPictures/FetchedDogPictures.re#L14
    // The one in that file uses Promise, but that's *wrong*.
    // We only used promise as a demo of its API. We'll remove it soon.

    // As you can see below, the pure XMLHttpRequest code is just as clean,
    // less mysterious for all, more performant, extensible, and actually correct.

    // Ignore these externals for now. They're just for illustration
    // purposes. I just copy pasted the Js code from
    // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
    // and translated it to Reason
    @@ -14,7 +22,7 @@ type response;

    [@bs.scope "JSON"][@bs.val] external parseResponse: response => {. "message": array(string)} = "parse";

    // ================ alright
    // ================ real parallel example to that linked file now

    type state =
    | LoadingDogs
  2. @chenglou chenglou created this gist Nov 10, 2019.
    78 changes: 78 additions & 0 deletions FetchedDogPictures.re
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,78 @@
    // hey you two! Ignore these externals for now. They're just for illustration
    // purposes. I just copy pasted the Js code from
    // https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Using_XMLHttpRequest
    // and translated it to Reason

    type request;
    type response;
    [@bs.new] external makeXMLHttpRequest: unit => request = "XMLHttpRequest";
    [@bs.send] external addEventListener: (request, string, unit => unit) => unit = "addEventListener";
    [@bs.get] external response: request => response = "response";
    [@bs.send] external open_: (request, string, string) => unit = "open";
    [@bs.send] external send: request => unit = "send";
    [@bs.send] external abort: request => unit = "abort";

    [@bs.scope "JSON"][@bs.val] external parseResponse: response => {. "message": array(string)} = "parse";

    // ================ alright

    type state =
    | LoadingDogs
    | ErrorFetchingDogs
    | LoadedDogs(array(string));

    [@react.component]
    let make = () => {
    let (state, setState) = React.useState(() => LoadingDogs);

    // Notice that instead of `useEffect`, we have `useEffect0`. See
    // reasonml.github.io/reason-react/docs/en/components#hooks for more info
    React.useEffect0(() => {
    let request = makeXMLHttpRequest()
    request->addEventListener("load", () => {
    setState(_previousState => LoadedDogs((request->response->parseResponse)##message));
    })
    request->addEventListener("error", () => {
    setState(_previousState => ErrorFetchingDogs);
    })
    request->open_("GET", "https://dog.ceo/api/breeds/image/random/3");
    request->send

    // the return value is called by React's useEffect when the component unmounts
    Some(() => {
    request->abort
    })
    });

    <div
    style={ReactDOMRe.Style.make(
    ~height="120px",
    ~display="flex",
    ~alignItems="center",
    ~justifyContent="center",
    (),
    )}>
    {switch (state) {
    | ErrorFetchingDogs => React.string("An error occurred!")
    | LoadingDogs => React.string("Loading...")
    | LoadedDogs(dogs) =>
    dogs
    ->Belt.Array.mapWithIndex((i, dog) => {
    let imageStyle =
    ReactDOMRe.Style.make(
    ~height="120px",
    ~width="100%",
    ~marginRight=i === Js.Array.length(dogs) - 1 ? "0px" : "8px",
    ~borderRadius="8px",
    ~boxShadow="0px 4px 16px rgb(200, 200, 200)",
    ~backgroundSize="cover",
    ~backgroundImage={j|url($dog)|j},
    ~backgroundPosition="center",
    (),
    );
    <div key=dog style=imageStyle />;
    })
    ->React.array
    }}
    </div>;
    };