Skip to content

Commit 86ae305

Browse files
authored
Merge pull request #2 from emeks-studio/feat/simplify-interface
Simplify deferred actions interface by using Rescript Core Map
2 parents 62ffca5 + f00fb28 commit 86ae305

File tree

8 files changed

+293
-230
lines changed

8 files changed

+293
-230
lines changed

HISTORY.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
## 1.0.0
1+
## 2.0.0
2+
3+
* Simplify API around deferred actions.
4+
5+
## 1.0.1
26

37
* Fork from `rescript-react-update`.
48
* Re-design and extend the API.

README.md

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,6 @@ As a consequence, of the fix, the library also introduce an elegant approach in
1010
A `deferred action` is an `action` that is not immediately dispatched, but rather scheduled to be dispatched later.
1111
In contrast with `reducers` (that given an action and the state, provides the new state), a `scheduler` is a function that given the current context and a deferred action, can execute (user defined) side effects, and return (or not) a cleanup/cancellation function associated with.
1212

13-
## Installation
14-
15-
```console
16-
$ yarn add rescript-react-restate
17-
```
18-
19-
or
20-
21-
```console
22-
$ npm install --save rescript-react-restate
23-
```
24-
25-
Then add `rescript-react-restate` to your `bsconfig.json` `bs-dependencies` field.
26-
2713
## Handling side effects (Asynchronous actions, logging, etc.)
2814

2915
`Restate` powers up reducers by allow them not just update state, but also defer an action to be dispatched later if is desired. This is useful when you want to handle side effects (like logging, network requests, etc.) after the state has been updated.
@@ -44,36 +30,23 @@ type deferredAction =
4430
| LogIncrement
4531
| LogDecrement
4632
47-
// Because of implementation details related on how we clean up the side effects, we need to provide a way to identify each deferred action. In other words, deferred actions, must have a unique identifier.
48-
module DeferredAction: Restate.HasDeferredAction with type t = deferredAction = {
49-
type t = deferredAction
50-
let variantId = action =>
51-
switch action {
52-
| LogIncrement => "LogIncrement"
53-
| LogDecrement => "LogDecrement"
54-
}
55-
}
56-
57-
// Instantiate the reducer with your deferred action type
58-
module RestateReducer = Restate.MakeReducer(DeferredAction)
59-
6033
// A Reducer now can update the state and schedule deferred actions (if they need to)
6134
let reducer = (state, action) =>
6235
switch action {
6336
| Increment =>
64-
RestateReducer.UpdateWithDeferred(
37+
Restate.UpdateWithDeferred(
6538
state + 1,
6639
LogIncrement,
6740
)
6841
| Decrement =>
69-
RestateReducer.UpdateWithDeferred(
42+
Restate.UpdateWithDeferred(
7043
state - 1,
7144
LogDecrement,
7245
)
7346
}
7447
7548
// A Scheduler handle deferred actions by triggering side effects and returning a cleanup function (if necessary)
76-
let scheduler: (RestateReducer.self<state, action>, deferredAction) => option<unit=>unit> =
49+
let scheduler: (Restate.self<state, action, deferredAction>, deferredAction) => option<unit=>unit> =
7750
(self, deferredAction) =>
7851
switch deferredAction {
7952
| LogIncrement =>
@@ -88,7 +61,7 @@ let scheduler: (RestateReducer.self<state, action>, deferredAction) => option<un
8861
8962
@react.component
9063
let make = () => {
91-
let (state, send, _defer) = RestateReducer.useReducer(reducer, scheduler, 0)
64+
let (state, send, _defer) = Restate.useReducer(reducer, scheduler, 0)
9265
<div>
9366
{state->React.int}
9467
<button onClick={_ => send(Decrement)}> {"-"->React.string} </button>
@@ -101,3 +74,28 @@ let make = () => {
10174

10275
If you'd rather initialize state lazily (if there's some computation you don't want executed at every render for instance), use `useReducerWithMapState` where the first argument is a function taking `unit` and returning the initial state.
10376

77+
## Installation
78+
79+
```console
80+
$ yarn add rescript-react-restate
81+
```
82+
83+
or
84+
85+
```console
86+
$ npm install --save rescript-react-restate
87+
```
88+
89+
Then add `rescript-react-restate` to your `bsconfig.json` `bs-dependencies` field.
90+
91+
## Development
92+
93+
```console
94+
nix develop
95+
96+
# Run examples server:
97+
yarn dev
98+
99+
# Build and watch rescript:
100+
yarn re:watch
101+
```

examples/BasicUsage.res

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -37,24 +37,16 @@ module ReactRestate = {
3737
type action = Tick | Reset
3838
type state = {elapsed: int}
3939
type deferredAction = ScheduleNextTick
40-
module DeferredAction: Restate.HasDeferredAction with type t = deferredAction = {
41-
type t = deferredAction
42-
let variantId = action =>
43-
switch action {
44-
| ScheduleNextTick => "ScheduleNextTick"
45-
}
46-
}
47-
module RestateReducer = Restate.MakeReducer(DeferredAction)
4840
let reducer = (state, action) =>
4941
switch action {
5042
| Tick =>
51-
RestateReducer.UpdateWithDeferred(
43+
Restate.UpdateWithDeferred(
5244
{elapsed: state.elapsed + 1},
5345
ScheduleNextTick
5446
)
55-
| Reset => RestateReducer.Update({elapsed: 0})
47+
| Reset => Restate.Update({elapsed: 0})
5648
}
57-
let scheduler: (RestateReducer.self<state, action>, deferredAction) => option<unit=>unit> =
49+
let scheduler: (Restate.self<state, action, 'deferredAction>, deferredAction) => option<unit=>unit> =
5850
(self, action) =>
5951
switch action {
6052
| ScheduleNextTick =>
@@ -67,7 +59,7 @@ module ReactRestate = {
6759
}
6860
@react.component
6961
let make = () => {
70-
let (state, send, _defer) = RestateReducer.useReducerWithMapState(reducer, scheduler, () => {elapsed: 0})
62+
let (state, send, _defer) = Restate.useReducerWithMapState(reducer, scheduler, () => {elapsed: 0})
7163
React.useEffect0(() => {
7264
send(Tick)
7365
None

examples/Counter_SideEffects.res

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -50,29 +50,21 @@ module ReactRestate = {
5050
type deferredAction =
5151
| LogIncrement
5252
| LogDecrement
53-
module DeferredAction: Restate.HasDeferredAction with type t = deferredAction = {
54-
type t = deferredAction
55-
let variantId = action =>
56-
switch action {
57-
| LogIncrement => "LogIncrement"
58-
| LogDecrement => "LogDecrement"
59-
}
60-
}
61-
module RestateReducer = Restate.MakeReducer(DeferredAction)
53+
6254
let reducer = (state, action) =>
6355
switch action {
6456
| Increment =>
65-
RestateReducer.UpdateWithDeferred(
57+
Restate.UpdateWithDeferred(
6658
state + 1,
6759
LogIncrement,
6860
)
6961
| Decrement =>
70-
RestateReducer.UpdateWithDeferred(
62+
Restate.UpdateWithDeferred(
7163
state - 1,
7264
LogDecrement,
7365
)
7466
}
75-
let scheduler: (RestateReducer.self<state, action>, deferredAction) => option<unit=>unit> =
67+
let scheduler: (Restate.self<state, action, deferredAction>, deferredAction) => option<unit=>unit> =
7668
(self, deferredAction) =>
7769
switch deferredAction {
7870
| LogIncrement =>
@@ -86,7 +78,7 @@ module ReactRestate = {
8678
}
8779
@react.component
8880
let make = () => {
89-
let (state, send, _defer) = RestateReducer.useReducer(reducer, scheduler, 0)
81+
let (state, send, _defer) = Restate.useReducer(reducer, scheduler, 0)
9082
<div>
9183
{state->React.int}
9284
<button onClick={_ => send(Decrement)}> {"-"->React.string} </button>

src/Internal_Iterator.res

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
type t<'a>
2+
3+
type value<'a> = {
4+
done: bool,
5+
value: option<'a>,
6+
}
7+
8+
@send external next: t<'a> => value<'a> = "next"
9+
external toArray: t<'a> => array<'a> = "Array.from"
10+
external toArrayWithMapper: (t<'a>, 'a => 'b) => array<'b> = "Array.from"
11+
12+
let forEach = (iterator, f) => {
13+
let iteratorDone = ref(false)
14+
15+
while !iteratorDone.contents {
16+
let {done, value} = iterator->next
17+
f(value)
18+
iteratorDone := done
19+
}
20+
}

src/Internal_Map.res

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Polyfill for Map taken from RescriptCore
2+
// Ref. https://github.com/rescript-association/rescript-core/blob/main/src/Core__Map.res
3+
4+
type t<'k, 'v> = Js.Map.t<'k, 'v>
5+
6+
@new external make: unit => t<'k, 'v> = "Map"
7+
@new external fromArray: array<('k, 'v)> => t<'k, 'v> = "Map"
8+
9+
@get external size: t<'k, 'v> => int = "size"
10+
11+
@send external clear: t<'k, 'v> => unit = "clear"
12+
13+
@send external forEach: (t<'k, 'v>, 'v => unit) => unit = "forEach"
14+
@send external forEachWithKey: (t<'k, 'v>, ('v, 'k) => unit) => unit = "forEach"
15+
16+
@send external get: (t<'k, 'v>, 'k) => option<'v> = "get"
17+
@send external has: (t<'k, 'v>, 'k) => bool = "has"
18+
@send external set: (t<'k, 'v>, 'k, 'v) => unit = "set"
19+
@send external delete: (t<'k, 'v>, 'k) => bool = "delete"
20+
21+
@send external keys: t<'k, 'v> => Internal_Iterator.t<'k> = "keys"
22+
@send external values: t<'k, 'v> => Internal_Iterator.t<'v> = "values"
23+
@send external entries: t<'k, 'v> => Internal_Iterator.t<('k, 'v)> = "entries"

0 commit comments

Comments
 (0)