Skip to content

Instantly share code, notes, and snippets.

@vbfox
Created January 28, 2018 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vbfox/5bae33e403821e2f9bdfb756f57877fe to your computer and use it in GitHub Desktop.
Save vbfox/5bae33e403821e2f9bdfb756f57877fe to your computer and use it in GitHub Desktop.
Experimentations with using requestAnimationFrame in fable elmish

If the Elmish state should be updated by the event but not the view a wrapper component might do:

[<Import("cloneElement", from="react")>]
let cloneElement(element: ReactElement, props: obj, [<ParamList>] children: obj) = jsNative

type RequestAnimationFrameState<'a> = {
    props: 'a
}

type RequestAnimationFrameComponent<'a>(initialProps) as this =
    inherit React.Component<'a, RequestAnimationFrameState<'a>>(initialProps)

    let mutable frameRequest = None
    do
        this.setInitState({ props = initialProps})

    override this.shouldComponentUpdate(nextProps, nextState) =
        let propsChanged = (* Compare keys in nextProps and this.props like PureComponent does *)
        if propsChanged then
            match frameRequest with
            | Some r ->
                window.cancelAnimationFrame r
            | _ -> ()

            frameRequest <- Some (window.requestAnimationFrame( fun _ -> this.setState({ props = this.props })))
        
        let stateChanged = not (obj.ReferenceEquals(this.state, nextState))
        stateChanged

    override this.render() =
        cloneElement(this.children.[0], this.state.props, [||])

If each event completly erase the previous one (last one wins) even updating the elmish state can be bypassed:

type Msg = | FrequentMsg of string

// Wrap a dispatcher to dispatch using `requestAnimationFrame` (If multiple dispatch are initiated within a frame only the latest one will apply)
let dispatchWithAnimationFrame (dispatch: Dispatch<_>) =
    let mutable lastRequest = None
    fun msg ->
        match lastRequest with
        | Some r -> window.cancelAnimationFrame r
        | _ -> ()

        lastRequest <- Some (window.requestAnimationFrame (fun _ -> dispatch msg))

let view dispatch =
    let onFastEvent arg = (dispatchWithAnimationFrame dispatch) (FrequentMsg arg)
    fun model ->
        // rest of render()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment