diff --git a/src/React.res b/src/React.res index 59f0bed..6072bfe 100644 --- a/src/React.res +++ b/src/React.res @@ -370,6 +370,60 @@ external useImperativeHandle7: ( ('a, 'b, 'c, 'd, 'e, 'f, 'g), ) => unit = "useImperativeHandle" +@module("react") external useId: unit => string = "useId" + +@module("react") external useDeferredValue: 'value => 'value = "useDeferredValue" + +@module("react") +external useTransition: unit => (bool, (. unit => unit) => unit) = "useTransition" + +@module("react") +external useInsertionEffect: (@uncurry (unit => option unit>)) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect0: (@uncurry (unit => option unit>), @as(json`[]`) _) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect1: (@uncurry (unit => option unit>), array<'a>) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect2: (@uncurry (unit => option unit>), ('a, 'b)) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect3: (@uncurry (unit => option unit>), ('a, 'b, 'c)) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect4: (@uncurry (unit => option unit>), ('a, 'b, 'c, 'd)) => unit = + "useInsertionEffect" +@module("react") +external useInsertionEffect5: ( + @uncurry (unit => option unit>), + ('a, 'b, 'c, 'd, 'e), +) => unit = "useInsertionEffect" +@module("react") +external useInsertionEffect6: ( + @uncurry (unit => option unit>), + ('a, 'b, 'c, 'd, 'e, 'f), +) => unit = "useInsertionEffect" +@module("react") +external useInsertionEffect7: ( + @uncurry (unit => option unit>), + ('a, 'b, 'c, 'd, 'e, 'f, 'g), +) => unit = "useInsertionEffect" + +@module("react") +external useSyncExternalStore: ( + @uncurry (unit => @uncurry (unit => unit)), + @uncurry unit => 'state, +) => 'state = "useSyncExternalStore" + +@module("react") +external useSyncExternalStoreWithServerSnapshot: ( + @uncurry (unit => @uncurry (unit => unit)), + @uncurry unit => 'state, + @uncurry unit => 'state, +) => 'state = "useSyncExternalStore" + module Uncurried = { @module("react") external useState: (@uncurry (unit => 'state)) => ('state, (. 'state => 'state) => unit) = @@ -436,14 +490,6 @@ module Uncurried = { ) => callback<'input, 'output> = "useCallback" } -type transitionConfig = {timeoutMs: int} - -@module("react") -external useTransition: ( - ~config: transitionConfig=?, - unit, -) => (callback, unit>, bool) = "useTransition" - @set external setDisplayName: (component<'props>, string) => unit = "displayName" diff --git a/src/ReactDOM.res b/src/ReactDOM.res index 5faa99b..fbf68c6 100644 --- a/src/ReactDOM.res +++ b/src/ReactDOM.res @@ -10,20 +10,20 @@ external querySelector: string => option = "document.querySelector" @module("react-dom") +@deprecated("ReactDOM.render is no longer supported in React 18. Use createRoot instead.") external render: (React.element, Dom.element) => unit = "render" -module Experimental = { - type root +module Root = { + type t - @module("react-dom") - external createRoot: Dom.element => root = "createRoot" + @send external render: (t, React.element) => unit = "render" - @module("react-dom") - external createBlockingRoot: Dom.element => root = "createBlockingRoot" - - @send external render: (root, React.element) => unit = "render" + @send external unmount: (t, unit) => unit = "unmount" } +@module("react-dom") +external createRoot: Dom.element => Root.t = "createRoot" + @module("react-dom") external hydrate: (React.element, Dom.element) => unit = "hydrate" diff --git a/test/React__test.res b/test/React__test.res index 682a684..f455a77 100644 --- a/test/React__test.res +++ b/test/React__test.res @@ -155,7 +155,11 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render DOM elements", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(
{"Hello world!"->React.string}
, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( +
{"Hello world!"->React.string}
, + ) + ) expect.bool( container->DOM.findBySelectorAndTextContent("div", "Hello world!")->Option.isSome, @@ -165,7 +169,7 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render null elements", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(
React.null
, container)) + act(() => ReactDOM.createRoot(container)->ReactDOM.Root.render(
React.null
)) expect.bool( container->DOM.findBySelectorAndPartialTextContent("div", "")->Option.isSome, @@ -175,7 +179,9 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render string elements", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(
{"Hello"->React.string}
, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render(
{"Hello"->React.string}
) + ) expect.bool( container->DOM.findBySelectorAndPartialTextContent("div", "Hello")->Option.isSome, @@ -185,7 +191,7 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render int elements", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(
{12345->React.int}
, container)) + act(() => ReactDOM.createRoot(container)->ReactDOM.Root.render(
{12345->React.int}
)) expect.bool( container->DOM.findBySelectorAndPartialTextContent("div", "12345")->Option.isSome, @@ -195,7 +201,9 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render float elements", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(
{12.345->React.float}
, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render(
{12.345->React.float}
) + ) expect.bool( container->DOM.findBySelectorAndPartialTextContent("div", "12.345")->Option.isSome, @@ -206,7 +214,9 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) let array = [1, 2, 3]->Array.map(item =>
{item->React.int}
) - act(() => ReactDOM.render(
{array->React.array}
, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render(
{array->React.array}
) + ) expect.bool( container->DOM.findBySelectorAndPartialTextContent("div", "1")->Option.isSome, @@ -225,9 +235,8 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) act(() => - ReactDOM.render( + ReactDOM.createRoot(container)->ReactDOM.Root.render( React.cloneElement(
{"Hello"->React.string}
, {"data-name": "World"}), - container, ) ) @@ -241,7 +250,7 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render react components", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(, container)) + act(() => ReactDOM.createRoot(container)->ReactDOM.Root.render()) expect.bool( container->DOM.findBySelectorAndTextContent("button", "0")->Option.isSome, @@ -268,7 +277,7 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render react components with reducers", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(, container)) + act(() => ReactDOM.createRoot(container)->ReactDOM.Root.render()) expect.bool( container->DOM.findBySelectorAndTextContent(".value", "0")->Option.isSome, @@ -312,7 +321,9 @@ describe("React", ({test, beforeEach, afterEach}) => { test("can render react components with reducers (map state)", ({expect}) => { let container = getContainer(container) - act(() => ReactDOM.render(, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render() + ) expect.bool( container->DOM.findBySelectorAndTextContent(".value", "1")->Option.isSome, @@ -357,9 +368,21 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) let callback = Mock.fn() - act(() => ReactDOM.render(, container)) - act(() => ReactDOM.render(, container)) - act(() => ReactDOM.render(, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) expect.value(callback->Mock.getMock->Mock.calls).toEqual([[0], [1]]) }) @@ -368,9 +391,21 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) let callback = Mock.fn() - act(() => ReactDOM.render(, container)) - act(() => ReactDOM.render(, container)) - act(() => ReactDOM.render(, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) expect.value(callback->Mock.getMock->Mock.calls).toEqual([[0], [1]]) }) @@ -387,7 +422,11 @@ describe("React", ({test, beforeEach, afterEach}) => { let myRef = ref(None) let callback = reactRef => myRef := Some(reactRef) - act(() => ReactDOM.render(, container)) + act(() => + ReactDOM.createRoot(container)->ReactDOM.Root.render( + , + ) + ) expect.value(myRef.contents->Option.map(item => item.current)).toEqual(Some(2)) }) @@ -396,11 +435,10 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) act(() => - ReactDOM.render( + ReactDOM.createRoot(container)->ReactDOM.Root.render(
{1->React.int}
{2->React.int}
{3->React.int}
, - container, ) ) @@ -421,9 +459,8 @@ describe("React", ({test, beforeEach, afterEach}) => { let container = getContainer(container) act(() => - ReactDOM.render( + ReactDOM.createRoot(container)->ReactDOM.Root.render( , - container, ) ) @@ -437,11 +474,10 @@ describe("React", ({test, beforeEach, afterEach}) => { let value = ref("") act(() => - ReactDOM.render( + ReactDOM.createRoot(container)->ReactDOM.Root.render( value := (event->ReactEvent.Form.target)["value"]} />, - container, ) ) @@ -461,7 +497,7 @@ describe("React", ({test, beforeEach, afterEach}) => { let consoleFn = Console.disableError() act(() => - ReactDOM.render( + ReactDOM.createRoot(container)->ReactDOM.Root.render( { switch error { @@ -474,7 +510,6 @@ describe("React", ({test, beforeEach, afterEach}) => { }}> , - container, ) )