A Fable.React hook that determines whether a component is currently visible on the screen.
Get it from NuGet!
This hook is useful when you need to display a large amount and you only want to render rows that are visible on the screen for performance reasons.
In this example, 5000 rows are displayed, and the (vertical) margin is set to 400px
(which extends the "visible" range above and below the visible screen):
This approach requires that your dynamically rendered row be broken out into its own component that utilizes the useIsVisible
hook:
open Feliz
open Feliz.UseIsVisible
[<ReactComponent>]
let Row (entry: Monthly.EntryView) =
let rowRef = React.useElementRef()
let isVisible = React.useIsVisible(rowRef, margin = 400, dependencies = [||])
if isVisible then
Html.tr [
prop.ref rowRef
prop.children [
Html.td [ str entry.Username ]
Html.td [ str (entry.Date.ToString("M/d/yyyy")) ]
Html.td [ str entry.Task ]
Html.td [ str (entry.Hours.ToString("0.00")) ]
Html.td [ str entry.Project ]
Html.td [ str entry.Email ]
Html.td [ str "8:00 AM" ]
]
]
else
Html.tr [
prop.ref rowRef
prop.children [
Html.td [
prop.colSpan 7
prop.children [
Html.b [
str "Loading..."
]
]
]
]
]
[<ReactComponent>]
let Row (entry: Monthly.EntryView) =
let rowRef = React.useElementRef()
let isVisible = React.useIsVisible(rowRef, margin = 400, dependencies = [||])
if isVisible then
tr [ Ref (adaptFelizUseElementRef rowRef) ] [
td [] [ str entry.Username ]
td [] [ str (entry.Date.ToString("M/d/yyyy")) ]
td [] [ str entry.Task ]
td [] [ str (entry.Hours.ToString("0.00")) ]
td [] [ str entry.Project ]
td [] [ str entry.Email ]
td [] [ str "8:00 AM" ]
]
else
tr [ Ref (adaptFelizUseElementRef rowRef) ] [
td [ ColSpan 7 ] [
b [] [ str "Loading..." ]
]
]
module HoursWorkedPage
open System
open Feliz
open Fable.React
open Fable.React.Props
open Feliz.UseIsVisible
[<ReactComponent>]
let Row (entry: Monthly.EntryView) =
let rowRef = React.useElementRef()
let isVisible = React.useIsVisible(rowRef, margin = 400)
if isVisible then
tr [ Ref (adaptFelizUseElementRef rowRef) ] [
td [] [ str entry.Username ]
td [] [ str (entry.Date.ToString("M/d/yyyy")) ]
td [] [ str entry.Task ]
td [] [ str (entry.Hours.ToString("0.00")) ]
td [] [ str entry.Project ]
td [] [ str entry.Email ]
td [] [ str "8:00 AM" ]
]
else
tr [ Ref (adaptFelizUseElementRef rowRef) ] [
td [ ColSpan 7 ] [
b [] [ str "Loading..." ]
]
]
[<ReactComponent>]
let Page (company: Company) =
let entries, setEntries = React.useState<Monthly.EntryView array>(Array.empty)
React.useEffectOnce(fun () ->
[|
for n in [1 .. 5000] do
{
EntryView.Date = DateTime.Today
EntryView.Email = "person@email.com"
EntryView.Hours = 8
EntryView.Project = $"Project %i{n}"
EntryView.Task = $"Task %i{n}"
EntryView.Username = $"User %i{n}"
}
|]
|> setEntries
)
let export() =
printfn "Exporting..."
Ctrls.container [
h4 [] [str "Hours Worked"]
Ctrls.row [
div [Class "col text-right"] [
Button.commandBarButton [Button.IconProps {| iconName = "ExcelDocument" |}; Button.Title "Download Excel"; Button.OnClick (fun e -> export())] [
str "Export to Excel"
]
]
]
Ctrls.row [
Ctrls.col [
table [Id "monthly-tbl"; Class "table"] [
thead [] [
tr [] [
th [Style [Width "100px"]] [str "BIM Detailer"]
th [Style [Width "70px"]] [str "Date"]
th [Style [Width "150px"]] [str "Task"]
th [Style [Width "70px"]] [str "Hours"]
th [Style [Width "150px"]] [str "Project"]
th [Style [Width "100px"]] [str "Email Address"]
th [Style [Width "100px"]] [str "Start Time"]
]
]
tbody [] [
for e in entries do
Row e
]
]
]
]
]
margin
allows you to specify a margin (in pixels) above and below the visible screen that will extend the "visible" region.dependencies
allows you to pass in anobj[]
of dependencies that will triggeruseIsVisible
to refresh if any of the dependency values change.