Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Slaying a UI Anti Pattern in ReasonML
/*
Slaying a UI Anti Pattern in ReasonML
Based on Kris Jenkins original writing.
http://blog.jenkster.com/2016/06/how-elm-slays-a-ui-antipattern.html
*/
type remoteData 'e 'a =
| NotAsked
| Loading
| Failure 'e
| Success 'a;
type user = {
id: int,
name: string
};
type state = {data: remoteData string (list user)};
let se = ReasonReact.stringToElement;
let fetchData () => Success [{id: 1, name: "foo"}, {id: 2, name: "bar"}];
let component = ReasonReact.reducerComponent "View";
let displayData data =>
ReasonReact.arrayToElement (
Array.of_list (
List.map
(fun {id, name} => <div key=(string_of_int id)> (se name) </div>) data
)
);
type action = remoteData string (list user);
/* ReasonReact.NoUpdate */
let make _ => {
...component,
initialState: fun () => {data: NotAsked},
reducer: fun action state =>
switch action {
| NotAsked => ReasonReact.NoUpdate /* If you skip a possible case, you will get a warning not an error */
| Loading => ReasonReact.Update {...state, data: Loading}
| Failure msg => ReasonReact.Update {...state, data: Failure msg}
| Success data => ReasonReact.Update {...state, data: Success data}
},
render: fun {state, reduce} =>
<div>
(
switch state.data {
| NotAsked =>
<div>
(se "Not Data Fetched Yet!")
<button
onClick=(
fun _evt => {
Js.Global.setTimeout (reduce (fun () => Loading)) 10;
Js.Global.setTimeout (reduce fetchData) 2000;
ignore ()
}
)>
(se "Fetch Data!")
</button>
</div>
| Loading => <div> (se "Loading Data...") </div>
| Failure msg => <div> (se ("Some Error: " ^ msg)) </div>
| Success data => <div> (se "Loaded Users") (displayData data) </div>
}
)
</div>
};
@dawehner

This comment has been minimized.

Copy link

commented Sep 11, 2017

I'm wondering how this works:

    switch action {
    | Loading => ReasonReact.Update {...state, data: Loading}
    | Failure msg => ReasonReact.Update {...state, data: Failure msg}
    | Success data => ReasonReact.Update {...state, data: Success data}
    },

... isn't there a case missing? I guess the data type of action is actually not defined but rather dynamically generated by the compiler and is different to remoteData itself?

@jaredly

This comment has been minimized.

Copy link

commented Sep 11, 2017

There is a case missing, and the compiler will generate a warning about "not all cases are matched" -- not an error though, interestingly. If you pass NotAsked to the reduce function, then a runtime error will occur ☹️ I'd rather the compiler call it an error

@busypeoples

This comment has been minimized.

Copy link
Owner Author

commented Sep 11, 2017

Yes, very true. You will get a warning not an error. Definitely should be an error. Saw the warning than forgot to update the example.
Will update accordingly.

@mlms13

This comment has been minimized.

Copy link

commented Jun 14, 2018

I'm several months late, but in case anyone else finds their way here, you can promote warnings to errors in your bsconfig.json. For example I have this:

  "warnings": {
    "error": "+8+11"
  }

This means that warning codes 8 (missing case in pattern match) and 11 (redundant case in pattern match) are treated as errors at compile time. The full list is here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.