Skip to main content
Version: 2.9.0

Using References

Within function components, React allows us to use the so-called references. There references are just like state variables except that changes in their (mutable) values do not cause a re-render in the function component lifetime. These mutable values can be used to hold a reference to DOM elements and manipulate it on a low-level as opposed to using the declarative API. For example, you can use the focus of an input element by a click of a button:

Show code
module Example.FocusInput

open Feliz
open Browser.Types

[<ReactComponent(true)>]
let FullFocusInputExample() =
// obtain a reference
let inputRef = React.useRef(None)

let focusTextInput() =
match inputRef.current with
| None -> ()
| Some element ->
let inputElement = unbox<HTMLInputElement> element
inputElement.focus()

Html.div [
Html.input [
prop.ref inputRef
prop.type'.text
]

Html.button [
prop.onClick (fun _ -> focusTextInput())
prop.text "Focus Input"
]
]

Here we use the React.useRef hook and provide it an initial value of None to get our inputRef which is of type IRefValue<'t option> at that point. Once you give inputRef to the input property prop.ref, the type of inputRef becomes more concrete and turns into IRefValue<HTMLElement option>. When we click on the button, the function focusTextInput() executes and makes the input element focused using the focus() function on that element but only after we unbox it into HTMLInputElement because otherwise a generic HTMLElement doesn't have that function.

IRefValue<'T> is defined as container type which has a settable property current : 'T

This is how React.useRef is used in modern React apps but it is a bit involved and you have to know the types of the elements. That is why I created three specialized variants of React.useRef to work with Html elements:

React.useInputRef : unit -> IRefValue<HTMLInputElement option>
React.useButtonRef : unit -> IRefValue<HTMLButtonElement option>
// the more generic version that works with any Html element
React.useElementRef : unit -> IRefValue<HTMLElement option>

Now the above example can be simplified into:

[<ReactComponent>]
let FocusInputExample() =
let inputRef = React.useInputRef()
let focusTextInput() = inputRef.current |> Option.iter (fun inputElement -> inputElement.focus())

Html.div [
Html.input [
prop.ref inputRef
prop.type'.text
]

Html.button [
prop.onClick (fun _ -> focusTextInput())
prop.text "Focus Input"
]
]

Forwarding Refs

You can also use React.forwardRef for passing refs to children components.

In most cases the above examples will suit your needs, but in some use-cases such as creating libraries for reusable components, it can be useful.

For more information see the React docs on forwarding refs.

Show code
module Example.ForwardRef

open Feliz

let ForwardRefChild = React.forwardRef(fun ((), ref) ->
Html.input [
prop.type'.text
prop.ref ref
])

[<ReactComponent(true)>]
let ForwardRefParent() =
let inputRef = React.useInputRef()

Html.div [
ForwardRefChild((), inputRef)
Html.button [
prop.text "Focus Input"
prop.onClick <| fun ev ->
inputRef.current
|> Option.iter (fun elem -> elem.focus())
]
]

Overriding behavior with useImperativeHandle

The hook React.useImperativeHandle allows you to override the behavior of the given ref.

This should be used in conjunction with React.forwardRef.

You should try to avoid using this when possible.

In this example we override the behavior of focus to instead set the text value of a div:

Show code
module Example.ImperativeHandle

open Feliz

let ForwardRefImperativeChild = React.forwardRef(fun ((), ref) ->
let divText,setDivText = React.useState ""
let inputRef = React.useInputRef()

React.useImperativeHandle(ref, fun () ->
inputRef.current
|> Option.map(fun innerRef ->
{| focus = fun () -> setDivText innerRef.className |})
)

Html.div [
Html.input [
prop.className "Howdy!"
prop.type'.text
prop.ref inputRef
]
Html.div [
prop.text divText
]
])

[<ReactComponent(true)>]
let ForwardRefImperativeParent() =
let ref = React.useRef<{| focus: unit -> unit |} option>(None)

Html.div [
ForwardRefImperativeChild((), ref)
Html.button [
prop.text "Focus Input"
prop.onClick <| fun ev ->
ref.current
|> Option.iter (fun elem -> elem.focus())
]
]