Skip to content

Instantly share code, notes, and snippets.

@busypeoples
Created August 18, 2017 15:25
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save busypeoples/847d908ee0ed8e501883b04e0f554435 to your computer and use it in GitHub Desktop.
Save busypeoples/847d908ee0ed8e501883b04e0f554435 to your computer and use it in GitHub Desktop.
Displaying different UI States nicely in Reason
/* Slaying a UI Anti Pattern in Reason */
type remoteData 'e 'a =
| Nothing
| Loading
| Failure 'e
| Success 'a;
type item = {
userId: int,
id: int,
title: string,
body: string
};
let parseListJson json :item => {
userId: Json.Decode.field "userId" Json.Decode.int json,
id: Json.Decode.field "id" Json.Decode.int json,
title: Json.Decode.field "title" Json.Decode.string json,
body: Json.Decode.field "body" Json.Decode.string json,
};
let parseListResponse json => Json.Decode.list parseListJson json;
let listUrl = "https://jsonplaceholder.typicode.com/posts?userId=1";
let fetchList () =>
Bs_fetch.fetch listUrl
|> Js.Promise.then_ Bs_fetch.Response.text
|> Js.Promise.then_ (fun jsonText =>
Js.Promise.resolve (parseListResponse(Js.Json.parseExn jsonText))
);
let se = ReasonReact.stringToElement;
let module RenderItems = {
let component = ReasonReact.statelessComponent "RenderItems";
let make ::items _ => {
...component,
render: fun self =>
<div className="items">
(ReasonReact.arrayToElement
(Array.of_list
(List.map (fun item => <div
key=(string_of_int item.id)
>
(se item.title)
</div>
) items)
)
)
</div>
};
};
type state = {
items: remoteData string (list item),
};
let handleListLoaded list _self => {
fetchList ()
|> Js.Promise.then_ (fun items => {
ReasonReact.Update {
items: Success list
};
Js.Promise.resolve ();
})
|> ignore;
};
let handleListOk state items _self => {
ReasonReact.Update {
...state,
items: Success items
};
};
let handleListError state e _self => {
ReasonReact.Update {
...state,
items: Failure e
};
};
let component = ReasonReact.statefulComponent "Data";
let make _ => {
...component,
initialState: fun () => {
items: Nothing
},
render: fun {state: {items}, update} => {
let view = switch (items) {
| Nothing => (se "Click to load data!")
| Loading => (se "Loading...")
| Failure e => (se e)
| Success items => <RenderItems items=items />
};
<div className="item">
<div>view</div>
<br />
<div>
<button
onClick=(update (fun text {state, update} => {
fetchList ()
|> Js.Promise.then_ (fun items => {
(update (handleListOk state)) items;
Js.Promise.resolve ();
})
|> Js.Promise.catch (fun _ => {
(update (handleListError state)) "Something went wrong!";
Js.Promise.resolve();
});
ReasonReact.Update {
...state,
items: Loading
};
}))
>
(se "click!")
</button>
</div>
</div>
}
};
@stereobooster
Copy link

stereobooster commented Aug 26, 2017

How about?

type remoteData 'c 'e 'a =
  | Nothing
  | Loading 'c
  | Failure 'e
  | Success 'a;

Where 'c stands for cancel token, like in this example facebook/react#8883 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment