├── .gitignore ├── .npmignore ├── README.md ├── bsconfig.json ├── bundle ├── index.html └── index.js ├── example ├── bsconfig.json ├── package.json ├── public │ └── index.html ├── src │ └── App.re └── yarn.lock ├── package.json ├── src ├── Hooks.re └── Hooks.rei └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | bundle 4 | .merlin 5 | .bsb.lock 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | exmaple 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hooks-experimental 2 | An experiment using react's new "hooks" with ReasonReact 3 | 4 | WARNING: Do not use for anything real. 5 | 6 | ## How to use 7 | 8 | ### 1) the package.json 9 | 10 | NOTE: You *must* use yarn, so that we can override reason-react's react dependencies with `resolutions`. 11 | 12 | ``` 13 | "resolutions": { 14 | "**/react": "16.7.0-alpha.0", 15 | "**/react-dom": "16.7.0-alpha.0" 16 | }, 17 | "dependencies": { 18 | "react": "16.7.0-alpha.0", 19 | "react-dom": "16.7.0-alpha.0", 20 | "reason-react": "^0.5.3", 21 | "hooks-experimental": "github:jaredly/hooks-experimental" 22 | } 23 | ``` 24 | 25 | ### 2) the bsconfig.json 26 | 27 | Nothing special here, just add `"hooks-experimental"` to `"bs-dependencies"`. 28 | 29 | ### 3) making a component! 30 | 31 | ```re 32 | module ReasonReact = Hooks.ReasonReact; 33 | 34 | module FancyCounter = { 35 | let component = (. props: {. "initialValue": int}) => { 36 | let (count, setCount) = Hooks.useState(props##initialValue); 37 | 38 |
39 |

{ReasonReact.string(string_of_int(count))}

40 | 43 |
; 44 | }; 45 | component->Hooks.setName("Just"); 46 | 47 | let make = (~initialValue, _children) => 48 | Hooks.createElement( 49 | ~component, 50 | ~props={"initialValue": initialValue}, 51 | ); 52 | }; 53 | 54 | ReactDOMRe.renderToElementWithId(, "root"); 55 | ``` 56 | 57 | ### 4) Hooks API 58 | 59 | ``` 60 | [@bs.module "react"] external useState: 'a => ('a, (. 'a) => unit) = ""; 61 | 62 | [@bs.module "react"] external useEffect: ((unit) => (unit => unit)) => unit = ""; 63 | [@bs.module "react"] 64 | external useMutationEffect: ((unit) => (unit => unit)) => unit = ""; 65 | [@bs.module "react"] 66 | external useLayoutEffect: ((unit) => (unit => unit)) => unit = ""; 67 | 68 | [@bs.module "react"] 69 | external useEffectWithoutCleanup: (unit => unit) => unit = "useEffect"; 70 | [@bs.module "react"] 71 | external useMutationEffectWithoutCleanup: (unit => unit) => unit = ""; 72 | [@bs.module "react"] 73 | external useLayoutEffectWithoutCleanup: (unit => unit) => unit = ""; 74 | 75 | [@bs.module "react"] external useCallback: (unit => 'a, 'b, unit) => 'a = ""; 76 | [@bs.module "react"] external useMemo: (unit => 'a, 'b) => 'a = ""; 77 | 78 | [@bs.module "react"] external useRef: 'a => {. "current": 'a} = ""; 79 | [@bs.module "react"] 80 | external useDomRef: unit => {. "current": option(Dom.node)} = "useRef"; 81 | 82 | [@bs.module "react"] 83 | external useReducer: 84 | (~reducer: (. 'state, 'action) => 'state, ~initial: 'state) => 85 | ('state, (. 'action) => unit) = 86 | ""; 87 | ``` -------------------------------------------------------------------------------- /bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-experimental", 3 | "sources": "src", 4 | "reason": { 5 | "react-jsx": 2 6 | }, 7 | "bs-dependencies": ["reason-react"], 8 | "refmt": 3 9 | } -------------------------------------------------------------------------------- /bundle/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Hooks! 4 | 5 |
6 | 7 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/bsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-example", 3 | "sources": "src", 4 | "reason": { 5 | "react-jsx": 2 6 | }, 7 | "bs-dependencies": ["reason-react", "hooks-experimental"], 8 | "refmt": 3 9 | } -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-example", 3 | "dependencies": { 4 | "bs-platform": "^4.0.7", 5 | "fpack": "^0.6.8", 6 | "react": "16.7.0-alpha.0", 7 | "react-dom": "16.7.0-alpha.0", 8 | "reason-react": "^0.5.3", 9 | "hooks-experimental": "file:../" 10 | }, 11 | "scripts": { 12 | "start": "bsb -make-world -w", 13 | "pack": "fpack serve lib/js/src/App.js -p 3010" 14 | }, 15 | "resolutions": { 16 | "**/react": "16.7.0-alpha.0", 17 | "**/react-dom": "16.7.0-alpha.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | React Hooks! 4 | 5 |
6 | 7 | 8 | -------------------------------------------------------------------------------- /example/src/App.re: -------------------------------------------------------------------------------- 1 | 2 | /* this file uses function components */ 3 | module ReasonReact = Hooks.ReasonReact; 4 | 5 | module Just = { 6 | let component = 7 | (. props: {. "initialValue": int}) => { 8 | let (count, setCount) = Hooks.useState(props##initialValue); 9 | let (show, setShown) = Hooks.useState(true); 10 | 11 |
12 | { 13 | show ? 14 |

{ReasonReact.string(string_of_int(count))}

15 | : ReasonReact.null 16 | } 17 | 20 | 23 |
; 24 | }; 25 | component->Hooks.setName("Just"); 26 | 27 | let make = (~initialValue, _children) => 28 | Hooks.createElement( 29 | ~component, 30 | ~props={"initialValue": initialValue}, 31 | ); 32 | }; 33 | 34 | ReactDOMRe.renderToElementWithId(, "root"); 35 | -------------------------------------------------------------------------------- /example/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | bs-platform@^4.0.7: 6 | version "4.0.7" 7 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-4.0.7.tgz#1a0bbf0fef439fef5f1a88ba60d5ac8a6d73570c" 8 | 9 | fpack@^0.6.8: 10 | version "0.6.8" 11 | resolved "https://registry.yarnpkg.com/fpack/-/fpack-0.6.8.tgz#a3c34b42a68c836ab0933e0cff1656ac39eb2092" 12 | 13 | "hooks-experimental@file:..": 14 | version "0.0.0-alpha" 15 | dependencies: 16 | bs-platform "^4.0.7" 17 | fpack "^0.6.8" 18 | react "16.7.0-alpha.0" 19 | react-dom "16.7.0-alpha.0" 20 | reason-react "^0.5.3" 21 | 22 | "js-tokens@^3.0.0 || ^4.0.0": 23 | version "4.0.0" 24 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 25 | 26 | loose-envify@^1.1.0, loose-envify@^1.3.1: 27 | version "1.4.0" 28 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 29 | dependencies: 30 | js-tokens "^3.0.0 || ^4.0.0" 31 | 32 | object-assign@^4.1.1: 33 | version "4.1.1" 34 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 35 | 36 | prop-types@^15.6.2: 37 | version "15.6.2" 38 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" 39 | dependencies: 40 | loose-envify "^1.3.1" 41 | object-assign "^4.1.1" 42 | 43 | react-dom@16.7.0-alpha.0, "react-dom@>=15.0.0 || >=16.0.0": 44 | version "16.7.0-alpha.0" 45 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.0.tgz#8379158d4c76d63c989f325f45dfa5762582584f" 46 | dependencies: 47 | loose-envify "^1.1.0" 48 | object-assign "^4.1.1" 49 | prop-types "^15.6.2" 50 | scheduler "^0.11.0-alpha.0" 51 | 52 | react@16.7.0-alpha.0, "react@>=15.0.0 || >=16.0.0": 53 | version "16.7.0-alpha.0" 54 | resolved "https://registry.yarnpkg.com/react/-/react-16.7.0-alpha.0.tgz#e2ed4abe6f268c9b092a1d1e572953684d1783a9" 55 | dependencies: 56 | loose-envify "^1.1.0" 57 | object-assign "^4.1.1" 58 | prop-types "^15.6.2" 59 | scheduler "^0.11.0-alpha.0" 60 | 61 | reason-react@^0.5.3: 62 | version "0.5.3" 63 | resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.5.3.tgz#10601809742fd991109ec9d69ad4baf2c3f17540" 64 | dependencies: 65 | react ">=15.0.0 || >=16.0.0" 66 | react-dom ">=15.0.0 || >=16.0.0" 67 | 68 | scheduler@^0.11.0-alpha.0: 69 | version "0.11.0-alpha.0" 70 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.0-alpha.0.tgz#7b132c726608993471db07866f2d59a52b9e190b" 71 | dependencies: 72 | loose-envify "^1.1.0" 73 | object-assign "^4.1.1" 74 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hooks-experimental", 3 | "version": "0.0.0-alpha.0", 4 | "dependencies": { 5 | "bs-platform": "^4.0.7", 6 | "fpack": "^0.6.8", 7 | "react": "16.7.0-alpha.0", 8 | "react-dom": "16.7.0-alpha.0", 9 | "reason-react": "^0.5.3" 10 | }, 11 | "scripts": { 12 | "build": "bsb -make-world" 13 | }, 14 | "resolutions": { 15 | "**/react": "16.7.0-alpha.0", 16 | "**/react-dom": "16.7.0-alpha.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Hooks.re: -------------------------------------------------------------------------------- 1 | [@bs.module "react"] 2 | external cloneElementOther: 3 | (ReasonReact.reactElement, 'props) => ReasonReact.reactElement = 4 | "cloneElement"; 5 | 6 | module ReasonReact = { 7 | include ReasonReact; 8 | let element = (~key=?, ~ref=?, element) => 9 | cloneElementOther(element, {"key": key, "ref": ref}); 10 | }; 11 | 12 | [@bs.set] external setName: ((. 'props) => ReasonReact.reactElement, string) => unit = "displayName"; 13 | 14 | [@bs.module "react"] external useState: 'a => ('a, (. 'a) => unit) = ""; 15 | 16 | [@bs.module "react"] external useEffect: ((unit) => ((. unit) => unit)) => unit = ""; 17 | [@bs.module "react"] 18 | external useMutationEffect: ((unit) => ((. unit) => unit)) => unit = ""; 19 | [@bs.module "react"] 20 | external useLayoutEffect: ((unit) => ((. unit) => unit)) => unit = ""; 21 | 22 | [@bs.module "react"] 23 | external useEffectWithoutCleanup: (unit => unit) => unit = "useEffect"; 24 | [@bs.module "react"] 25 | external useMutationEffectWithoutCleanup: (unit => unit) => unit = ""; 26 | [@bs.module "react"] 27 | external useLayoutEffectWithoutCleanup: (unit => unit) => unit = ""; 28 | 29 | [@bs.module "react"] external useCallback: (unit => 'a, 'b, unit) => 'a = ""; 30 | [@bs.module "react"] external useMemo: (unit => 'a, 'b) => 'a = ""; 31 | 32 | [@bs.module "react"] external useRef: 'a => {. "current": 'a} = ""; 33 | [@bs.module "react"] 34 | external useDomRef: unit => {. "current": option(Dom.node)} = "useRef"; 35 | 36 | [@bs.module "react"] 37 | external useReducer: 38 | (~reducer: (. 'state, 'action) => 'state, ~initial: 'state) => 39 | ('state, (. 'action) => unit) = 40 | ""; 41 | 42 | [@bs.module "react"] 43 | external createElement: 44 | ( 45 | ~component: (. Js.t({..} as 'a)) => ReasonReact.reactElement, 46 | ~props: Js.t({..} as 'a) 47 | ) => 48 | ReasonReact.reactElement = 49 | ""; 50 | 51 | /* Not doing useImperativeMethods because it's super confusing. */ -------------------------------------------------------------------------------- /src/Hooks.rei: -------------------------------------------------------------------------------- 1 | let cloneElementOther: 2 | (ReasonReact.reactElement, 'props) => ReasonReact.reactElement; 3 | 4 | module ReasonReact: { 5 | include (module type of ReasonReact) with type reactElement = ReasonReact.reactElement; 6 | let element: 7 | (~key: 'a=?, ~ref: 'b=?, ReasonReact.reactElement) => 8 | ReasonReact.reactElement; 9 | }; 10 | 11 | /* let setName: ((. 'props) => ReasonReact.reactElement, string) => unit; 12 | let useState: 'a => ('a, (. 'a) => unit); 13 | let useEffect: ((unit, unit) => unit) => unit; 14 | let useMutationEffect: ((unit, unit) => unit) => unit; 15 | let useLayoutEffect: ((unit, unit) => unit) => unit; 16 | let useEffectWithoutCleanup: (unit => unit) => unit; 17 | let useMutationEffectWithoutCleanup: (unit => unit) => unit; 18 | let useLayoutEffectWithoutCleanup: (unit => unit) => unit; 19 | let useCallback: (unit => 'a, 'b, unit) => 'a; 20 | let useMemo: (unit => 'a, 'b) => 'a; 21 | let useRef: 'a => {. "current": 'a}; 22 | let useDomRef: unit => {. "current": option(Dom.node)}; 23 | let useReducer: 24 | (~reducer: ('state, 'action) => 'state, ~initial: 'state) => 25 | ('state, 'action => unit); 26 | let createElement: 27 | ( 28 | ~component: (. Js.t({..} as 'a)) => ReasonReact.reactElement, 29 | ~props: Js.t('a) 30 | ) => 31 | ReasonReact.reactElement; */ 32 | 33 | 34 | 35 | 36 | [@bs.set] external setName: ((. 'props) => ReasonReact.reactElement, string) => unit = "displayName"; 37 | 38 | [@bs.module "react"] external useState: 'a => ('a, (. 'a) => unit) = ""; 39 | 40 | 41 | [@bs.module "react"] external useEffect: ((unit) => ((. unit) => unit)) => unit = ""; 42 | [@bs.module "react"] 43 | external useMutationEffect: ((unit) => ((. unit) => unit)) => unit = ""; 44 | [@bs.module "react"] 45 | external useLayoutEffect: ((unit) => ((. unit) => unit)) => unit = ""; 46 | 47 | 48 | [@bs.module "react"] 49 | external useEffectWithoutCleanup: (unit => unit) => unit = "useEffect"; 50 | [@bs.module "react"] 51 | external useMutationEffectWithoutCleanup: (unit => unit) => unit = ""; 52 | [@bs.module "react"] 53 | external useLayoutEffectWithoutCleanup: (unit => unit) => unit = ""; 54 | 55 | [@bs.module "react"] external useCallback: (unit => 'a, 'b, unit) => 'a = ""; 56 | [@bs.module "react"] external useMemo: (unit => 'a, 'b) => 'a = ""; 57 | 58 | [@bs.module "react"] external useRef: 'a => {. "current": 'a} = ""; 59 | [@bs.module "react"] 60 | external useDomRef: unit => {. "current": option(Dom.node)} = "useRef"; 61 | 62 | [@bs.module "react"] 63 | external useReducer: 64 | (~reducer: (. 'state, 'action) => 'state, ~initial: 'state) => 65 | ('state, (. 'action) => unit) = 66 | ""; 67 | 68 | [@bs.module "react"] 69 | external createElement: 70 | ( 71 | ~component: (. Js.t({..} as 'a)) => ReasonReact.reactElement, 72 | ~props: Js.t({..} as 'a) 73 | ) => 74 | ReasonReact.reactElement = 75 | ""; -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | bs-platform@^4.0.7: 6 | version "4.0.7" 7 | resolved "https://registry.yarnpkg.com/bs-platform/-/bs-platform-4.0.7.tgz#1a0bbf0fef439fef5f1a88ba60d5ac8a6d73570c" 8 | 9 | fpack@^0.6.8: 10 | version "0.6.8" 11 | resolved "https://registry.yarnpkg.com/fpack/-/fpack-0.6.8.tgz#a3c34b42a68c836ab0933e0cff1656ac39eb2092" 12 | 13 | "js-tokens@^3.0.0 || ^4.0.0": 14 | version "4.0.0" 15 | resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" 16 | 17 | loose-envify@^1.1.0, loose-envify@^1.3.1: 18 | version "1.4.0" 19 | resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" 20 | dependencies: 21 | js-tokens "^3.0.0 || ^4.0.0" 22 | 23 | object-assign@^4.1.1: 24 | version "4.1.1" 25 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" 26 | 27 | prop-types@^15.6.2: 28 | version "15.6.2" 29 | resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102" 30 | dependencies: 31 | loose-envify "^1.3.1" 32 | object-assign "^4.1.1" 33 | 34 | react-dom@16.7.0-alpha.0, "react-dom@>=15.0.0 || >=16.0.0": 35 | version "16.7.0-alpha.0" 36 | resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.7.0-alpha.0.tgz#8379158d4c76d63c989f325f45dfa5762582584f" 37 | dependencies: 38 | loose-envify "^1.1.0" 39 | object-assign "^4.1.1" 40 | prop-types "^15.6.2" 41 | scheduler "^0.11.0-alpha.0" 42 | 43 | react@16.7.0-alpha.0, "react@>=15.0.0 || >=16.0.0": 44 | version "16.7.0-alpha.0" 45 | resolved "https://registry.yarnpkg.com/react/-/react-16.7.0-alpha.0.tgz#e2ed4abe6f268c9b092a1d1e572953684d1783a9" 46 | dependencies: 47 | loose-envify "^1.1.0" 48 | object-assign "^4.1.1" 49 | prop-types "^15.6.2" 50 | scheduler "^0.11.0-alpha.0" 51 | 52 | reason-react@^0.5.3: 53 | version "0.5.3" 54 | resolved "https://registry.yarnpkg.com/reason-react/-/reason-react-0.5.3.tgz#10601809742fd991109ec9d69ad4baf2c3f17540" 55 | dependencies: 56 | react ">=15.0.0 || >=16.0.0" 57 | react-dom ">=15.0.0 || >=16.0.0" 58 | 59 | scheduler@^0.11.0-alpha.0: 60 | version "0.11.0-alpha.0" 61 | resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.11.0-alpha.0.tgz#7b132c726608993471db07866f2d59a52b9e190b" 62 | dependencies: 63 | loose-envify "^1.1.0" 64 | object-assign "^4.1.1" 65 | --------------------------------------------------------------------------------