From cb8ba82e8863fb71f2c6797ebde44f7b314afc8e Mon Sep 17 00:00:00 2001 From: Javier Chavarri Date: Fri, 3 Nov 2023 00:00:55 +0000 Subject: [PATCH 1/3] typed keys 2 --- ppx/reason_react_ppx.ml | 12 +++++++++--- src/React.re | 6 ++++-- src/React.rei | 6 ++++-- src/ReactDOM.re | 2 +- src/ReactDOM.rei | 2 +- test/Hooks__test.re | 2 +- test/React__test.re | 18 ++++++++++++------ 7 files changed, 32 insertions(+), 16 deletions(-) diff --git a/ppx/reason_react_ppx.ml b/ppx/reason_react_ppx.ml index c3c8bd4e0..1d2941819 100644 --- a/ppx/reason_react_ppx.ml +++ b/ppx/reason_react_ppx.ml @@ -54,6 +54,12 @@ module Binding = struct { txt = Longident.Ldot (Lident "React", "array"); loc }) [ (nolabel, children) ] + let unsafeArray ~loc children = + Builder.pexp_apply ~loc + (Builder.pexp_ident ~loc + { txt = Longident.Ldot (Lident "React", "unsafeArray"); loc }) + [ (nolabel, children) ] + let componentLike ~loc props return = Ptyp_constr ( { loc; txt = Ldot (Lident "React", "componentLike") }, @@ -448,7 +454,7 @@ let jsxExprAndChildren ~ident ~loc ~ctxt mapper ~keyProps children = when list = [] -> ( Builder.pexp_ident ~loc { loc; txt = Ldot (ident, "jsxKeyed") }, Some (label, key), - Some (Binding.React.array ~loc children) ) + Some (Binding.React.unsafeArray ~loc children) ) | Some (ListLiteral { pexp_desc = Pexp_array list }), [] when list = [] -> ( Builder.pexp_ident ~loc { loc; txt = Ldot (ident, "jsx") }, None, @@ -458,13 +464,13 @@ let jsxExprAndChildren ~ident ~loc ~ctxt mapper ~keyProps children = children *) ( Builder.pexp_ident ~loc { loc; txt = Ldot (ident, "jsxsKeyed") }, Some (label, key), - Some (Binding.React.array ~loc children) ) + Some (Binding.React.unsafeArray ~loc children) ) | Some (ListLiteral children), [] -> (* this is a hack to support react components that introspect into their children *) ( Builder.pexp_ident ~loc { loc; txt = Ldot (ident, "jsxs") }, None, - Some (Binding.React.array ~loc children) ) + Some (Binding.React.unsafeArray ~loc children) ) | None, (label, key) :: _ -> ( Builder.pexp_ident ~loc { loc; txt = Ldot (ident, "jsxKeyed") }, Some (label, key), diff --git a/src/React.re b/src/React.re index 873d87347..4242995e5 100644 --- a/src/React.re +++ b/src/React.re @@ -301,6 +301,7 @@ module Event = { }; type element; +type elementKeyed; type componentLike('props, 'return) = 'props => 'return; type component('props) = componentLike('props, element); @@ -308,7 +309,8 @@ external null: element = "null"; external float: float => element = "%identity"; external int: int => element = "%identity"; external string: string => element = "%identity"; -external array: array(element) => element = "%identity"; +external array: array(elementKeyed) => element = "%identity"; +external unsafeArray: array(element) => element = "%identity"; /* this function exists to prepare for making `component` abstract */ external component: componentLike('props, element) => component('props) = @@ -328,7 +330,7 @@ external createElementVariadic: [@mel.module "react/jsx-runtime"] external jsxKeyed: - (component('props), 'props, ~key: string=?, unit) => element = + (component('props), 'props, ~key: string=?, unit) => elementKeyed = "jsx"; [@mel.module "react/jsx-runtime"] diff --git a/src/React.rei b/src/React.rei index fcbdb1120..a5d849bae 100644 --- a/src/React.rei +++ b/src/React.rei @@ -1,4 +1,5 @@ type element; +type elementKeyed; type componentLike('props, 'return) = 'props => 'return; type component('props) = componentLike('props, element); @@ -6,7 +7,8 @@ external null: element = "null"; external float: float => element = "%identity"; external int: int => element = "%identity"; external string: string => element = "%identity"; -external array: array(element) => element = "%identity"; +external array: array(elementKeyed) => element = "%identity"; +external unsafeArray: array(element) => element = "%identity"; /* this function exists to prepare for making `component` abstract */ external component: componentLike('props, element) => component('props) = @@ -26,7 +28,7 @@ external createElementVariadic: [@mel.module "react/jsx-runtime"] external jsxKeyed: - (component('props), 'props, ~key: string=?, unit) => element = + (component('props), 'props, ~key: string=?, unit) => elementKeyed = "jsx"; [@mel.module "react/jsx-runtime"] diff --git a/src/ReactDOM.re b/src/ReactDOM.re index 42b3a5777..5a7ee6854 100644 --- a/src/ReactDOM.re +++ b/src/ReactDOM.re @@ -1535,7 +1535,7 @@ external createDOMElementVariadic: "createElement"; [@mel.module "react/jsx-runtime"] -external jsxKeyed: (string, domProps, ~key: string=?, unit) => React.element = +external jsxKeyed: (string, domProps, ~key: string=?, unit) => React.elementKeyed = "jsx"; [@mel.module "react/jsx-runtime"] diff --git a/src/ReactDOM.rei b/src/ReactDOM.rei index d6cbd26d4..c4ca10615 100644 --- a/src/ReactDOM.rei +++ b/src/ReactDOM.rei @@ -1536,7 +1536,7 @@ external createDOMElementVariadic: "createElement"; [@mel.module "react/jsx-runtime"] -external jsxKeyed: (string, domProps, ~key: string=?, unit) => React.element = +external jsxKeyed: (string, domProps, ~key: string=?, unit) => React.elementKeyed = "jsx"; [@mel.module "react/jsx-runtime"] diff --git a/test/Hooks__test.re b/test/Hooks__test.re index 20b942a46..aba4a17a4 100644 --- a/test/Hooks__test.re +++ b/test/Hooks__test.re @@ -55,7 +55,7 @@ module DummyStatefulComponent = { let make = (~initialValue=0, ()) => { let (value, setValue) = React.useState(() => initialValue); - ; }; diff --git a/test/React__test.re b/test/React__test.re index 8b26c8339..e9f44053d 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -283,9 +283,12 @@ describe("React", () => { act(() => { ReactDOM.Client.render( root, - -
"Child"->React.string
-
, + [| + +
"Child"->React.string
+
, + |] + ->React.array, ) }); @@ -309,9 +312,12 @@ describe("React", () => { }; let render = author => -
-
-
; + [| +
+
+
, + |] + ->React.array; act(() => { ReactDOM.Client.render( From 28a707235ee2a9af201b40b0410c638ce1aabdaf Mon Sep 17 00:00:00 2001 From: Javier Chavarri Date: Fri, 3 Nov 2023 11:59:21 +0000 Subject: [PATCH 2/3] add jsxskeyed to the mix --- src/React.re | 2 +- src/React.rei | 2 +- src/ReactDOM.re | 2 +- src/ReactDOM.rei | 2 +- test/React__test.re | 41 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/src/React.re b/src/React.re index 4242995e5..419b4d6a2 100644 --- a/src/React.re +++ b/src/React.re @@ -341,7 +341,7 @@ external jsxs: (component('props), 'props) => element = "jsxs"; [@mel.module "react/jsx-runtime"] external jsxsKeyed: - (component('props), 'props, ~key: string=?, unit) => element = + (component('props), 'props, ~key: string=?, unit) => elementKeyed = "jsxs"; [@mel.module "react/jsx-runtime"] external jsxFragment: 'element = "Fragment"; diff --git a/src/React.rei b/src/React.rei index a5d849bae..ecd352dde 100644 --- a/src/React.rei +++ b/src/React.rei @@ -39,7 +39,7 @@ external jsxs: (component('props), 'props) => element = "jsxs"; [@mel.module "react/jsx-runtime"] external jsxsKeyed: - (component('props), 'props, ~key: string=?, unit) => element = + (component('props), 'props, ~key: string=?, unit) => elementKeyed = "jsxs"; [@mel.module "react/jsx-runtime"] external jsxFragment: 'element = "Fragment"; diff --git a/src/ReactDOM.re b/src/ReactDOM.re index 5a7ee6854..be5e64140 100644 --- a/src/ReactDOM.re +++ b/src/ReactDOM.re @@ -1545,5 +1545,5 @@ external jsx: (string, domProps) => React.element = "jsx"; external jsxs: (string, domProps) => React.element = "jsxs"; [@mel.module "react/jsx-runtime"] -external jsxsKeyed: (string, domProps, ~key: string=?, unit) => React.element = +external jsxsKeyed: (string, domProps, ~key: string=?, unit) => React.elementKeyed = "jsxs"; diff --git a/src/ReactDOM.rei b/src/ReactDOM.rei index c4ca10615..f2639ac78 100644 --- a/src/ReactDOM.rei +++ b/src/ReactDOM.rei @@ -1546,5 +1546,5 @@ external jsx: (string, domProps) => React.element = "jsx"; external jsxs: (string, domProps) => React.element = "jsxs"; [@mel.module "react/jsx-runtime"] -external jsxsKeyed: (string, domProps, ~key: string=?, unit) => React.element = +external jsxsKeyed: (string, domProps, ~key: string=?, unit) => React.elementKeyed = "jsxs"; diff --git a/test/React__test.re b/test/React__test.re index e9f44053d..92f864307 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -364,3 +364,44 @@ describe("React", () => { () }; }); + +module Foo = { + [@react.component] + let make = () => { +
; + }; +}; + +module Bar = { + [@react.component] + let make = () => { + let ks = Array.init(10, string_of_int); +
{Array.map(ks, id => )->React.array}
; + }; +}; + +let _ = +
+ {[|1, 2, 3|] + ->Array.map(id => { + +
+
+
+ + }) + ->React.array} +
; + +let _ = +
+ {[|1, 2, 3|] + ->Array.map(id => { +
+
+
+
+
+ }) + ->React.array} +
; From 29fc9be70078036f476e63b0993a81c12ec923d3 Mon Sep 17 00:00:00 2001 From: Javier Chavarri Date: Fri, 3 Nov 2023 12:19:46 +0000 Subject: [PATCH 3/3] fix children.map and cloneelement --- src/React.re | 6 +++++- src/React.rei | 6 +++++- test/React__test.re | 10 +++++++++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/React.re b/src/React.re index 419b4d6a2..89cfb52f0 100644 --- a/src/React.re +++ b/src/React.re @@ -323,6 +323,10 @@ external createElement: (component('props), 'props) => element = [@mel.module "react"] external cloneElement: (element, 'props) => element = "cloneElement"; +[@mel.module "react"] +external cloneElementWithKey: (element, {.. "key": string}) => elementKeyed = + "cloneElement"; + [@mel.splice] [@mel.module "react"] external createElementVariadic: (component('props), 'props, array(element)) => element = @@ -367,7 +371,7 @@ module Children = { external map: (element, element => element) => element = "map"; [@mel.module "react"] [@mel.scope "Children"] external mapWithIndex: - (element, [@mel.uncurry] ((element, int) => element)) => element = + (element, [@mel.uncurry] ((element, int) => elementKeyed)) => element = "map"; [@mel.module "react"] [@mel.scope "Children"] external forEach: (element, element => unit) => unit = "forEach"; diff --git a/src/React.rei b/src/React.rei index ecd352dde..de5328245 100644 --- a/src/React.rei +++ b/src/React.rei @@ -21,6 +21,10 @@ external createElement: (component('props), 'props) => element = [@mel.module "react"] external cloneElement: (element, 'props) => element = "cloneElement"; +[@mel.module "react"] +external cloneElementWithKey: (element, {.. "key": string}) => elementKeyed = + "cloneElement"; + [@mel.splice] [@mel.module "react"] external createElementVariadic: (component('props), 'props, array(element)) => element = @@ -65,7 +69,7 @@ module Children: { external map: (element, element => element) => element = "map"; [@mel.module "react"] [@mel.scope "Children"] external mapWithIndex: - (element, [@mel.uncurry] ((element, int) => element)) => element = + (element, [@mel.uncurry] ((element, int) => elementKeyed)) => element = "map"; [@mel.module "react"] [@mel.scope "Children"] external forEach: (element, element => unit) => unit = "forEach"; diff --git a/test/React__test.re b/test/React__test.re index 92f864307..748eaa685 100644 --- a/test/React__test.re +++ b/test/React__test.re @@ -22,7 +22,7 @@ module DummyComponentThatMapsChildren = { let make = (~children, ()) => {
{children->React.Children.mapWithIndex((element, index) => { - React.cloneElement( + React.cloneElementWithKey( element, {"key": {j|$index|j}, "data-index": index}, ) @@ -405,3 +405,11 @@ let _ = }) ->React.array}
; + +[@react.component] +let make = (~children) => +
    + {children->React.Children.mapWithIndex((element, index) => +
  1. element
  2. + )} +
;