Skip to content

Commit

Permalink
Merge branch '19' of github.com:/reasonml/reason-react into 19
Browse files Browse the repository at this point in the history
* '19' of github.com:/reasonml/reason-react:
  Enable ref as valid prop (#862)
  Replace react dom's testing library with ReactTestingLibrary (#859)
  • Loading branch information
davesnx committed Nov 25, 2024
2 parents ea1f768 + 08f5e19 commit 05ecc63
Show file tree
Hide file tree
Showing 32 changed files with 5,894 additions and 835 deletions.
18 changes: 17 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ jest: ## Run the jest unit tests
jest-watch: ## Run the jest unit tests in watch mode
@npx jest --watch

.PHONY: jest-devtools
jest-devtools: ## Run the jest unit tests in watch mode
@echo "open Chrome and go to chrome://inspect"
@node --inspect-brk node_modules/.bin/jest --runInBand --detectOpenHandles

.PHONY: test
test: ## Run the runtests from dune (snapshot)
@$(DUNE) build @runtest
Expand All @@ -54,11 +59,22 @@ format-check: ## Checks if format is correct
.PHONY: install
install: ## Update the package dependencies when new deps are added to dune-project
@opam install . --deps-only --with-test
@npm install
# --force is needed because we are installing react@19 while other dependencies
# require react@18. It's a good workaround to bypass the npm validation error
# and test the rc versions of React
@npm install --force

.PHONY: init
create-switch: ## Create a local opam switch
@opam switch create . 5.2.0 --no-install

.PHONY: init
init: create-switch install ## Create a local opam switch, install deps

.PHONY: demo-watch
demo-watch: ## Build the demo in watch mode
@$(DUNE) build @melange-app --watch

.PHONY: demo-serve
demo-serve: ## Build the demo and serve it
npx http-server -p 8080 _build/default/demo/
9 changes: 9 additions & 0 deletions demo/dune
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
(melange.emit
(target demo)
(alias melange-app)
(module_systems
(es6 mjs))
(libraries reason-react melange.belt melange.dom)
(runtime_deps index.html)
(preprocess
(pps melange.ppx reason-react-ppx)))
42 changes: 42 additions & 0 deletions demo/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Demo reason-react</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/yegor256/tacit@gh-pages/tacit-css-1.8.1.min.css" />
<style>
body {
padding: 2rem;
}

#root {
margin-left: auto;
margin-right: auto;
width: 900px;
}
</style>
<script type="importmap">
{
"imports": {
"melange/": "./demo/node_modules/melange/",
"melange.belt/": "./demo/node_modules/melange.belt/",
"melange.js/": "./demo/node_modules/melange.js/",
"reason-react/": "./demo/node_modules/reason-react/",
"react/jsx-runtime": "https://esm.sh/[email protected]/jsx-runtime",
"react": "https://esm.sh/[email protected]",
"react-dom": "https://esm.sh/[email protected]",
"react-dom/client": "https://esm.sh/[email protected]/client"
}
}
</script>
</head>

<body>
<div id="root"></div>
</body>
<script type="module" src="./demo/demo/main.mjs"></script>

</html>
187 changes: 187 additions & 0 deletions demo/main.re
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
module Stateful = {
[@react.component]
let make = (~title, ~initialValue=0, ~children=React.null) => {
let (value, setValue) = React.useState(() => initialValue);
let onClick = _ => setValue(value => value + 1);

<section>
<h3> {React.string(title)} </h3>
<button key="asdf" onClick> value->React.int </button>
children
</section>;
};
};

module Reducer = {
type action =
| Increment
| Decrement;

[@react.component]
let make = (~initialValue=0) => {
let (state, send) =
React.useReducer(
(state, action) =>
switch (action) {
| Increment => state + 1
| Decrement => state - 1
},
initialValue,
);

Js.log2("Reducer state", state);

<section>
<h3> {React.string("React.useReducer")} </h3>
<main> state->React.int </main>
<button onClick={_ => send(Increment)}>
"Increment"->React.string
</button>
<button onClick={_ => send(Decrement)}>
"Decrement"->React.string
</button>
</section>;
};
};

module ReducerWithMapState = {
type action =
| Increment
| Decrement;

[@react.component]
let make = (~initialValue=0) => {
let (state, send) =
React.useReducerWithMapState(
(state, action) =>
switch (action) {
| Increment => state + 1
| Decrement => state - 1
},
initialValue,
initialValue => initialValue + 75,
);

Js.log2("ReducerWithMapState state", state);

<section>
<h3> {React.string("React.useReducerWithMapState")} </h3>
<main> state->React.int </main>
<button onClick={_ => send(Increment)}>
"Increment"->React.string
</button>
<button onClick={_ => send(Decrement)}>
"Decrement"->React.string
</button>
</section>;
};
};

module WithEffect = {
[@react.component]
let make = (~value) => {
React.useEffect1(
() => {
Js.log("useEffect");
None;
},
[|value|],
);

React.string("React.useEffect");
};
};

module RerenderOnEachClick = {
[@react.component]
let make = (~value=0, ~callback as _) => {
let (value, setValue) = React.useState(() => value);
let onClick = _ =>
if (value < 3) {
Js.log2("Clicked with:", value);
setValue(value => value + 1);
} else {
Js.log("Max value reached, not firing a rerender");
setValue(value => value);
};

<section>
<h3> {React.string("RerenderOnEachClick")} </h3>
<button onClick> <WithEffect value /> </button>
</section>;
};
};

module WithLayoutEffect = {
[@react.component]
let make = (~value=0, ~callback) => {
React.useLayoutEffect1(
() => {
callback(value);
Js.log("useLayoutEffect");
None;
},
[|value|],
);

<section> <h3> {React.string("React.useLayoutEffect")} </h3> </section>;
};
};

module WithRefAndEffect = {
[@react.component]
let make = (~callback) => {
let myRef = React.useRef(1);
React.useEffect0(() => {
myRef.current = myRef.current + 1;
callback(myRef);
None;
});

<section>
<h3> {React.string("React.useRef and useEffect")} </h3>
<main> {React.int(myRef.current)} </main>
</section>;
};
};

[@mel.module "react"]
external useReducer:
([@mel.uncurry] (('state, 'action) => 'state), 'state) =>
('state, 'action => unit) =
"useReducer";

module UseReducerNoProblemo = {
[@react.component]
let make = () => {
let reducer = (v, _) => v + 1;
let (state, send) = useReducer(reducer, 0);
Js.log("asdfasd");
<button onClick={_ => send(0)}> {React.int(state)} </button>;
};
};

module App = {
[@react.component]
let make = (~initialValue) => {
let value = 99;
let callback = _number => ();

<main>
<Stateful title="Stateful" initialValue />
<Reducer key="reducer" initialValue />
<ReducerWithMapState key="reducer-with-map-state" initialValue />
<RerenderOnEachClick key="rerender-on-each-click" value=0 callback />
<WithLayoutEffect key="layout-effect" value callback />
<WithRefAndEffect key="ref-and-effect" callback />
<UseReducerNoProblemo />
</main>;
};
};

switch (ReactDOM.querySelector("#root")) {
| Some(el) =>
let root = ReactDOM.Client.createRoot(el);
ReactDOM.Client.render(root, <App initialValue=0 />);
| None => Js.log("No root element found")
};
2 changes: 1 addition & 1 deletion dune
Original file line number Diff line number Diff line change
@@ -1 +1 @@
(dirs src test ppx)
(dirs src test ppx demo)
Loading

0 comments on commit 05ecc63

Please sign in to comment.