Zaid-Ajaj / Feliz

A fresh retake of the React API in Fable and a collection of high-quality components to build React applications in F#, optimized for happiness
https://zaid-ajaj.github.io/Feliz/
MIT License
540 stars 78 forks source link

React 18 UseEffectOnce #480

Closed JordanMarr closed 1 year ago

JordanMarr commented 2 years ago

With React 18 bringing a breaking change that will cause React.useEffect to fire twice, it would probably be a good idea to update the Feliz useEffectOnce to prevent breakage.

Here is an adaptation from https://dev.to/ag-grid/react-18-avoiding-use-effect-getting-called-twice-4i9e:


type React =

    member this.useEffectOnce(effect: unit -> unit) = 
        let calledOnce = React.useRef false

        React.useEffect (fun () -> 
            if calledOnce.current 
            then ()
            else
                calledOnce.current <- true
                effect()
        , [||])

    member this.useEffectOnce(effect: unit -> IDisposable) = 
        let destroyFunc = React.useRef None
        let calledOnce = React.useRef false
        let renderAfterCalled = React.useRef false

        if calledOnce.current then
            renderAfterCalled.current <- true

        React.useEffect (fun () -> 
            if calledOnce.current 
            then None
            else
                calledOnce.current <- true
                destroyFunc.current <- effect() |> Some

                if renderAfterCalled.current
                then destroyFunc.current
                else None
        , [||])

    member this.useEffectOnce(effect: unit -> IDisposable option) = 
        let destroyFunc = React.useRef None
        let calledOnce = React.useRef false
        let renderAfterCalled = React.useRef false

        if calledOnce.current then
            renderAfterCalled.current <- true

        React.useEffect (fun () -> 
            if calledOnce.current 
            then None
            else
                calledOnce.current <- true
                destroyFunc.current <- effect()

                if renderAfterCalled.current
                then destroyFunc.current
                else None
        , [||])

Probably needs a bit more work, but that's the idea anyway.

Thoughts?

Zaid-Ajaj commented 1 year ago

Implemented in Feliz v2.0 🚀 (which depends on React v18)