Skip to content

Instantly share code, notes, and snippets.

@AlexSchwabauer
Created January 24, 2018 16:18
Show Gist options
  • Save AlexSchwabauer/5ddf6a4bf2149f67adcbda87d34a4a07 to your computer and use it in GitHub Desktop.
Save AlexSchwabauer/5ddf6a4bf2149f67adcbda87d34a4a07 to your computer and use it in GitHub Desktop.
Functor Reason React Example
module type OrderFilterArrayType = {
type t;
type filterName;
let reduceFilter: (filterName, array(t)) => array(t);
type itemID;
let getItemID: t => itemID;
let findOneIndex: (itemID, array(t)) => option(int);
};
module Make = (Item: OrderFilterArrayType) => {
type item = Item.t;
module Component = {
type sortFn = (item, item) => int;
type orderAction =
| Randomize
| Reverse
| Reorder(array(item))
| Sort(sortFn);
type action =
| Filter(Item.filterName)
| ResetFilter
| DeleteItem(Item.itemID)
| DeleteAll
| AddItems(array(item))
| MutateOrder(orderAction);
type state = {
items: array(item),
filterHistory: array(Item.filterName)
};
type renderProps = {
ordered: array(item),
filtered: array(item),
reverse: unit => unit,
randomize: unit => unit,
reorder: array(item) => unit,
filter: Item.filterName => unit,
resetFilter: unit => unit,
deleteItem: Item.itemID => unit,
deleteAll: unit => unit,
addItems: array(item) => unit,
filterHistory: array(Item.filterName),
sortCustomFn: sortFn => unit
};
type renderFn = renderProps => ReasonReact.reactElement;
/* quick and dirty */
let shuffleArray: array('a) => array('a) = [%bs.raw
"function shuffle (array) {\n var rng = Math.random\n \n var result = []\n \n for (var i = 0; i < array.length; ++i) {\n var j = Math.floor(rng() * (i + 1))\n \n if (j !== i) {\n result[i] = result[j]\n }\n \n result[j] = array[i]\n }\n \n return result\n }"
];
/* apply all filters in the history one after the other */
let applyFilters = (items, filterHistory) =>
filterHistory |> Array.fold_left((items, filter) => Item.reduceFilter(filter, items), items);
let component = ReasonReact.reducerComponent("OrderFilterArrayComponent");
let make = (~render: renderFn, ~items as initialItems: array(item), _children) => {
...component,
initialState: () => {items: initialItems, filterHistory: [||]},
reducer: (action, state) =>
ReasonReact.(
switch action {
| MutateOrder(Reverse) => Update({...state, items: Js.Array.reverseInPlace(state.items)})
| MutateOrder(Sort(sortFn)) =>
Update({...state, items: state.items |> Js.Array.sortInPlaceWith(sortFn)})
| MutateOrder(Randomize) => Update({...state, items: shuffleArray(state.items)})
| MutateOrder(Reorder(items)) => Update({...state, items})
| Filter(name) =>
Update({...state, filterHistory: Array.append(state.filterHistory, [|name|])})
| ResetFilter => Update({...state, filterHistory: [||]})
| AddItems(items) =>
/* only add unique items. Maybe better with a Set */
let currentIDs = state.items |> Array.map(Item.getItemID);
Update({
...state,
items:
Array.append(
state.items,
items
|> Js.Array.filter(
(item) => ! (currentIDs |> Js.Array.includes(Item.getItemID(item)))
)
)
})
| DeleteAll => Update({...state, items: [||]})
| DeleteItem(id) =>
switch (Item.findOneIndex(id, state.items)) {
| None => NoUpdate
| Some(index) =>
Update({...state, items: state.items |> Js.Array.filteri((_, i) => index !== i)})
}
}
),
render: ({send, state}) =>
render({
sortCustomFn: (sortFn) => send(MutateOrder(Sort(sortFn))),
ordered: state.items,
filtered: applyFilters(state.items, state.filterHistory),
reorder: (a) => send(MutateOrder(Reorder(a))),
reverse: () => send(MutateOrder(Reverse)),
filter: (a) => send(Filter(a)),
deleteItem: (a) => send(DeleteItem(a)),
deleteAll: (_) => send(DeleteAll),
addItems: (a) => send(AddItems(a)),
resetFilter: (_) => send(ResetFilter),
filterHistory: state.filterHistory,
randomize: () => send(MutateOrder(Randomize))
})
};
};
};
module ImageType = {
type t = Types.Image.t;
type filterName =
| Commented;
let reduceFilter = (f, items: array(t)) =>
switch f {
| Commented => items |> Js.Array.filter((img) => img##hasComments);
};
type itemID = int;
let getItemID = (item: t) => item##id;
let findOneIndex = (id, items) =>
switch (items |> Js.Array.findIndex((item) => item##id === id)) {
| (-1) => None
| a => Some(a)
};
};
module Gallery = Functor.Make(ImageType);
/* then the application somewhere in a react tree...*/
Gallery.(
<Component
items=myImageGalleryArray
render=(
({ordered, addItems, deleteAll, deleteItem, reverse, randomize, reorder, sortCustomFn}) => ReasonReact.stringToElement("Render something with all the render props")
)
/>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment