fable-compiler / fable-react

Fable bindings and helpers for React and React Native
MIT License
275 stars 66 forks source link

ReactDOM.hydrate is no longer supported in React 18 #227

Closed forki closed 2 years ago

forki commented 2 years ago

ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

@kerams I assume Program.withReactHydrate needs to be changed!?

kerams commented 2 years ago

Yup, I intend to create a PR for elmish-react tonight to use the new API when React 18 is detected. I believe you can ignore the warning for now.

kerams commented 2 years ago

@forki, but if you're in a hurry, you can try defining this yourself and calling it instead of Program.withReactHydrate.

let withReactHydrateUsing lazyView2With placeholderId (program:Elmish.Program<_,_,_,_>) =
    let setState model dispatch =
        ReactDomClient.hydrateRoot (
            Browser.Dom.document.getElementById placeholderId,
            lazyView2With (fun x y -> obj.ReferenceEquals(x,y)) (Program.view program) model dispatch
        )

    program
    |> Program.withSetState setState

let withReactHydrate placeholderId (program:Elmish.Program<_,_,_,_>) =
    withReactHydrateUsing lazyView2With placeholderId program

In fact, it would actually help me later if you could check it works.

forki commented 2 years ago

image

kerams commented 2 years ago

Hmm, unfortunately I have no experience with SSR. I do wonder why it says you are calling createRoot though.

kerams commented 2 years ago

The upgrade guide only says you need to do this, which is what I have translated above

// Before
import { hydrate } from 'react-dom';
const container = document.getElementById('app');
hydrate(<App tab="home" />, container);

// After
import { hydrateRoot } from 'react-dom/client';
const container = document.getElementById('app');
const root = hydrateRoot(container, <App tab="home" />);
// Unlike with createRoot, you don't need a separate root.render() call here.

Maybe you need to update the server too? But again, https://github.com/reactwg/react-18/discussions/22 mentions nothing.

kerams commented 2 years ago

@forki, aha. https://github.com/reactwg/react-18/discussions/5

If you want to update a root again after hydration, you can save it to a variable, just like with createRoot, and call root.render() later:

The fact that it said you didn't need a separate render call confused me. So hydrateRoot also returns a root that you use to render in every setState call I guess?

fourglobe302500 commented 2 years ago

Hi my code keeps logging this err, any idea of how I can fix it?

Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17

kerams commented 2 years ago

If you use Fable.React 8+, you should not see that message.

fourglobe302500 commented 2 years ago

Didn't solve it

kerams commented 2 years ago

@fourglobe302500, sorry I meant Fable.Elmish.React 4.0.0-beta-2.

fourglobe302500 commented 2 years ago

It worked thanks

alfonsogarciacaro commented 2 years ago

Thanks a lot for the help @kerams and @fourglobe302500 for confirming! 👍 Let's close this then, please reopen if you still have issues.

smoothdeveloper commented 10 months ago

I'm having very similar warning but for ReactDOM.render:

ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot

This is via using Fable.React.Helpers.mountById, am I supposed to use something else?

Here is my paket.lock

STORAGE: NONE
NUGET
  remote: https://api.nuget.org/v3/index.json
    Fable.AST (4.3) - restriction: >= netstandard2.0
    Fable.Browser.Blob (1.3) - restriction: >= netstandard2.0
      Fable.Core (>= 3.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Browser.Dom (2.15)
      Fable.Browser.Blob (>= 1.3) - restriction: >= netstandard2.0
      Fable.Browser.Event (>= 1.5) - restriction: >= netstandard2.0
      Fable.Browser.WebStorage (>= 1.2) - restriction: >= netstandard2.0
      Fable.Core (>= 3.2.8) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Browser.Event (1.5) - restriction: >= netstandard2.0
      Fable.Browser.Gamepad (>= 1.1) - restriction: >= netstandard2.0
      Fable.Core (>= 3.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Browser.Gamepad (1.2) - restriction: >= netstandard2.0
      Fable.Core (>= 3.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Browser.WebStorage (1.2) - restriction: >= netstandard2.0
      Fable.Browser.Event (>= 1.5) - restriction: >= netstandard2.0
      Fable.Core (>= 3.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Core (4.2)
    Fable.Elmish (4.1) - restriction: >= netstandard2.0
      Fable.Core (>= 3.7.1) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Elmish.HMR (7.0)
      Fable.Core (>= 3.7.1) - restriction: >= netstandard2.0
      Fable.Elmish.React (>= 4.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.Elmish.React (4.0) - restriction: >= netstandard2.0
      Fable.Elmish (>= 4.0) - restriction: >= netstandard2.0
      Fable.ReactDom.Types (>= 18.0) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.FontAwesome (3.0) - restriction: >= netstandard2.0
      Fable.Core (>= 3.7.1) - restriction: >= netstandard2.0
      Fable.React (>= 9.2) - restriction: >= netstandard2.0
    Fable.FontAwesome.Free (3.0)
      Fable.Core (>= 3.7.1) - restriction: >= netstandard2.0
      Fable.FontAwesome (>= 3.0) - restriction: >= netstandard2.0
      Fable.React (>= 9.2) - restriction: >= netstandard2.0
    Fable.React (9.3)
      Fable.React.Types (>= 18.3) - restriction: >= netstandard2.0
      Fable.ReactDom.Types (>= 18.2) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.React.Types (18.3) - restriction: >= netstandard2.0
      Fable.Browser.Dom (>= 2.4.4) - restriction: >= netstandard2.0
      Fable.Core (>= 3.2.7) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Fable.ReactDom.Types (18.2) - restriction: >= netstandard2.0
      Fable.React.Types (>= 18.3) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Feliz (2.7)
      Fable.ReactDom.Types (>= 18.2) - restriction: >= netstandard2.0
      Feliz.CompilerPlugins (>= 2.2) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Feliz.CompilerPlugins (2.2) - restriction: >= netstandard2.0
      Fable.AST (>= 4.2.1) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    Feliz.Recharts (4.2)
      Feliz (>= 2.3) - restriction: >= netstandard2.0
      FSharp.Core (>= 4.7.2) - restriction: >= netstandard2.0
    FSharp.Core (8.0.100)
    Fulma (3.0)
      Fable.Core (>= 3.7.1) - restriction: >= netstandard2.0
      Fable.React (>= 9.2) - restriction: >= netstandard2.0
MangelMaxime commented 10 months ago

@smoothdeveloper You are supposed to use createRoot which mountById doesn't use (yet?).

Example: https://github.com/elmish/react/blob/v4.x/src/react.fs#L23C15-L49

I suppose we could make Fable.React.Helpers.mountById detect the version of React and use the expected API based on that without introducing a breaking change.

smoothdeveloper commented 10 months ago

@MangelMaxime thanks, for now I can still try to figure out what I'm doing (very new to the F# on browser frontend) and hope I can better understand the react stuff and the whole underlying stuff.

I also need to refresh the element and I'm not sure mountById is the correct call.

I've tried with the Feliz ReactDOM helper and end up having no warning on initial mount, but when I refresh the ReactDOM.createRoot and render calls cause another warning.

You are calling ReactDOMClient.createRoot() on a container that has already been passed to createRoot() before. Instead, call root.render() on the existing root instead if you want to update it.

But this is maybe specific to the Feliz helper, not this repository.

I suppose we could make Fable.React.Helpers.mountById detect the version of React and use the expected API based on that without introducing a breaking change.

Having a custom warning for fable-react users that guide how to transform the call in F# terms would be great to guide the users that aren't necessarily familar with the react thing.

I'll spend time looking at the elmish and fable-react documentation to level up a bit.

MangelMaxime commented 10 months ago

But this is maybe specific to the Feliz helper, not this repository

This is because if you try to call twice createRoot on the same element then React complains. He is telling you that you are doing twice the same things.

If I remember correctly, React expects you to either update the existing root or destroy the previous one and then instantiate the new one.

smoothdeveloper commented 10 months ago

@MangelMaxime, thanks, it sounds I need to keep track of the instanciated roots in some form of state, and use/reuse those then.