Skip to content

Commit aacbe11

Browse files
committed
Initial commit
0 parents  commit aacbe11

11 files changed

+332
-0
lines changed

.gitattributes

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Tell github that .re and .rei files are Reason
2+
*.re linguist-language=Reason
3+
*.rei linguist-language=Reason

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.DS_Store
2+
.merlin
3+
.bsb.lock
4+
npm-debug.log
5+
/lib/bs/
6+
/node_modules/
7+
*.bs.js

HISTORY.md

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0
2+
3+
Initial release

README.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# reason-react-update
2+
3+
> useReducer with updates and side effects!
4+
5+
## Installation
6+
7+
```console
8+
$ yarn add reason-react-update
9+
```
10+
11+
or
12+
13+
```console
14+
$ npm install --save reason-react-update
15+
```
16+
17+
Then add `reason-react-update` to your `bsconfig.json` `bs-dependencies` field.
18+
19+
## ReactUpdate.useReducer
20+
21+
```reason
22+
type state = int;
23+
24+
type action =
25+
| Increment
26+
| Decrement;
27+
28+
[@react.component]
29+
let make = () => {
30+
let (state, send) =
31+
ReactUpdate.useReducer(0, (action, state) =>
32+
switch (action) {
33+
| Increment => Update(state + 1)
34+
| Decrement => Update(state + 1)
35+
}
36+
);
37+
<div>
38+
{state->Js.String.make->React.string}
39+
<button onClick={_ => send(Decrement)}> "-"->React.string </button>
40+
<button onClick={_ => send(Increment)}> "+"->React.string </button>
41+
</div>;
42+
};
43+
```
44+
45+
### Cancelling a side effect
46+
47+
The callback you pass to `SideEffects` & `UpdateWithSideEffect` returns an `option(unit => unit)`, which is the cancellation function.
48+
49+
```reason
50+
// doesn't cancel
51+
SideEffects(({send}) => {
52+
Js.log(1);
53+
None
54+
});
55+
// cancels
56+
SideEffects(({send}) => {
57+
let request = Request.make();
58+
request->Future.get(payload => send(Receive(payload)))
59+
Some(() => {
60+
Request.cancel(request)
61+
})
62+
});
63+
```

bsconfig.json

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"name": "reason-react-update",
3+
"reason": {
4+
"react-jsx": 3
5+
},
6+
"sources": [
7+
{
8+
"dir": "src",
9+
"subdirs": true
10+
},
11+
{
12+
"dir": "examples",
13+
"subdirs": true,
14+
"type": "dev"
15+
}
16+
],
17+
"package-specs": [
18+
{
19+
"module": "es6",
20+
"in-source": true
21+
}
22+
],
23+
"suffix": ".bs.js",
24+
"bs-dependencies": ["reason-react"],
25+
"refmt": 3
26+
}

examples/BasicUsage.re

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
type action =
2+
| Tick
3+
| Reset;
4+
5+
type state = {elapsed: int};
6+
7+
[@react.component]
8+
let make = () => {
9+
let (state, send) =
10+
ReactUpdate.useReducer({elapsed: 0}, (action, state) =>
11+
switch (action) {
12+
| Tick =>
13+
UpdateWithSideEffects(
14+
{elapsed: state.elapsed + 1},
15+
({send}) => {
16+
let timeoutId = Js.Global.setTimeout(() => send(Tick), 1_000);
17+
Some(() => Js.Global.clearTimeout(timeoutId));
18+
},
19+
)
20+
| Reset => Update({elapsed: 0})
21+
}
22+
);
23+
React.useEffect0(() => {
24+
send(Tick);
25+
None;
26+
});
27+
<div>
28+
{state.elapsed->Js.String.make->React.string}
29+
<button onClick={_ => send(Reset)}>
30+
"Reset"->React.string
31+
</button>
32+
</div>;
33+
};

examples/Counter.re

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
type state = int;
2+
3+
type action =
4+
| Increment
5+
| Decrement;
6+
7+
[@react.component]
8+
let make = () => {
9+
let (state, send) =
10+
ReactUpdate.useReducer(0, (action, state) =>
11+
switch (action) {
12+
| Increment => Update(state + 1)
13+
| Decrement => Update(state + 1)
14+
}
15+
);
16+
<div>
17+
{state->Js.String.make->React.string}
18+
<button onClick={_ => send(Decrement)}> "-"->React.string </button>
19+
<button onClick={_ => send(Increment)}> "+"->React.string </button>
20+
</div>;
21+
};

package.json

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"name": "reason-react-update",
3+
"version": "0.1.0",
4+
"scripts": {
5+
"build": "bsb -make-world",
6+
"start": "bsb -make-world -w",
7+
"clean": "bsb -clean-world",
8+
"test": "bsb -make-world"
9+
},
10+
"bugs": "https://github.com/bloodyowl/reason-react-update/issues",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/bloodyowl/reason-react-update.git"
14+
},
15+
"keywords": [
16+
"reason-react"
17+
],
18+
"author": "bloodyowl <[email protected]>",
19+
"license": "MIT",
20+
"peerDependencies": {
21+
"react": "^16.8.0",
22+
"react-dom": "^16.8.0",
23+
"reason-react": ">=0.7.0"
24+
},
25+
"devDependencies": {
26+
"bs-platform": "^5.0.1",
27+
"react": "^16.8.0",
28+
"react-dom": "^16.8.0",
29+
"reason-react": ">=0.7.0"
30+
}
31+
}

src/ReactUpdate.re

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
open Belt;
2+
3+
type update('action, 'state) =
4+
| NoUpdate
5+
| Update('state)
6+
| UpdateWithSideEffects(
7+
'state,
8+
self('action, 'state) => option(unit => unit),
9+
)
10+
| SideEffects(self('action, 'state) => option(unit => unit))
11+
and self('action, 'state) = {
12+
send: 'action => unit,
13+
state: 'state,
14+
}
15+
and fullState('action, 'state) = {
16+
state: 'state,
17+
sideEffects: ref(array(self('action, 'state) => option(unit => unit))),
18+
};
19+
20+
let useReducer = (initialState, reducer) => {
21+
let ({state, sideEffects}, send) =
22+
React.useReducer(
23+
({state, sideEffects} as fullState, action) =>
24+
switch (reducer(action, state)) {
25+
| NoUpdate => fullState
26+
| Update(state) => {...fullState, state}
27+
| UpdateWithSideEffects(state, sideEffect) => {
28+
state,
29+
sideEffects: ref(Array.concat(sideEffects^, [|sideEffect|])),
30+
}
31+
| SideEffects(sideEffect) => {
32+
...fullState,
33+
sideEffects:
34+
ref(Array.concat(fullState.sideEffects^, [|sideEffect|])),
35+
}
36+
},
37+
{state: initialState, sideEffects: ref([||])},
38+
);
39+
React.useEffect1(
40+
() =>
41+
if (Array.length(sideEffects^) > 0) {
42+
let cancelFuncs =
43+
Array.keepMap(sideEffects^, func => func({state, send}));
44+
sideEffects := [||];
45+
Array.length(cancelFuncs) > 0
46+
? Some(() => cancelFuncs->Array.forEach(func => func())) : None;
47+
} else {
48+
None;
49+
},
50+
[|sideEffects|],
51+
);
52+
(state, send);
53+
};

src/ReactUpdate.rei

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
type update('action, 'state) =
2+
| NoUpdate
3+
| Update('state)
4+
| UpdateWithSideEffects('state, self('action, 'state) => option(unit => unit))
5+
| SideEffects(self('action, 'state) => option(unit => unit))
6+
and self('action, 'state) = {
7+
send: 'action => unit,
8+
state: 'state,
9+
}
10+
and fullState('action, 'state) = {
11+
state: 'state,
12+
sideEffects: ref(array(self('action, 'state) => option(unit => unit))),
13+
};
14+
15+
let useReducer:
16+
('state, ('action, 'state) => update('action, 'state)) =>
17+
('state, 'action => unit);

yarn.lock

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
bs-platform@^5.0.1:
6+
version "5.0.3"
7+
resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-5.0.3.tgz#2b167603ef52574cb9534fabab702f6013715e6c"
8+
integrity sha512-GAeypBebeDGTay5kJ3v5Z3Whp1Q4zQ0hAttflVtPG3zps88xDZnVAlS3JGIIBDmJFEMyNtv5947a/IWKvWXcPw==
9+
10+
"js-tokens@^3.0.0 || ^4.0.0":
11+
version "4.0.0"
12+
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
13+
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
14+
15+
loose-envify@^1.1.0, loose-envify@^1.4.0:
16+
version "1.4.0"
17+
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
18+
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
19+
dependencies:
20+
js-tokens "^3.0.0 || ^4.0.0"
21+
22+
object-assign@^4.1.1:
23+
version "4.1.1"
24+
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
25+
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
26+
27+
prop-types@^15.6.2:
28+
version "15.7.2"
29+
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
30+
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
31+
dependencies:
32+
loose-envify "^1.4.0"
33+
object-assign "^4.1.1"
34+
react-is "^16.8.1"
35+
36+
react-dom@>=16.8.1, react-dom@^16.8.0:
37+
version "16.8.6"
38+
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.8.6.tgz#71d6303f631e8b0097f56165ef608f051ff6e10f"
39+
integrity sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==
40+
dependencies:
41+
loose-envify "^1.1.0"
42+
object-assign "^4.1.1"
43+
prop-types "^15.6.2"
44+
scheduler "^0.13.6"
45+
46+
react-is@^16.8.1:
47+
version "16.8.6"
48+
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
49+
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
50+
51+
react@>=16.8.1, react@^16.8.0:
52+
version "16.8.6"
53+
resolved "https://registry.yarnpkg.com/react/-/react-16.8.6.tgz#ad6c3a9614fd3a4e9ef51117f54d888da01f2bbe"
54+
integrity sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==
55+
dependencies:
56+
loose-envify "^1.1.0"
57+
object-assign "^4.1.1"
58+
prop-types "^15.6.2"
59+
scheduler "^0.13.6"
60+
61+
reason-react@>=0.7.0:
62+
version "0.7.0"
63+
resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.7.0.tgz#46a975c321e81cd51310d7b1a02418ca7667b0d6"
64+
integrity sha512-czR/f0lY5iyLCki9gwftOFF5Zs40l7ZSFmpGK/Z6hx2jBVeFDmIiXB8bAQW/cO6IvtuEt97OmsYueiuOYG9XjQ==
65+
dependencies:
66+
react ">=16.8.1"
67+
react-dom ">=16.8.1"
68+
69+
scheduler@^0.13.6:
70+
version "0.13.6"
71+
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.13.6.tgz#466a4ec332467b31a91b9bf74e5347072e4cd889"
72+
integrity sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==
73+
dependencies:
74+
loose-envify "^1.1.0"
75+
object-assign "^4.1.1"

0 commit comments

Comments
 (0)