permalinkWindowing with React Virtuoso

The browser's UI can suffer from performance issues when burdened with an excessive number of elements a common drawback of the DOM (Domain Object Model).

While it is usually unnecessary to have a multitude of elements on a single page, there are cases where long lists with numerous children can arise. This situation often occurs with infinite-scroll pages like Facebook or Twitter, as well as typeahead inputs featuring extensive autocomplete options.

In many instances, particularly in the aforementioned scenarios, it is not essential for all children to be simultaneously visible. By treating the scroll container as a window into the complete list of items, a technique known as windowing, we can optimize performance. This approach involves rendering only the currently visible children, effectively enhancing the user experience.

One notable windowing library that excels in this realm is react-virtuoso. Here's a sample implementation using PureScript:

Here's an example of how to use it with PureScript:

Terminal
spago install react-virtuoso

permalinkMinimal example

/src/Example/ReactVirtuoso.purs
module Example.ReactVirtuoso where

import Prelude
import React.Basic.Hooks as React
import React.Virtuoso (virtuoso, virtuosoImpl)
import React.Basic.DOM (css) as R
import Data.Function.Uncurried (mkFn3)
import React.Basic.DOM.Simplified.Generated (div) as R
import React.Basic.Events (handler_)
import Data.Monoid (power) as Monoid
import Data.Array (replicate) as Array
import React.Basic (ReactComponent)
import Effect.Unsafe (unsafePerformEffect)
import Effect (Effect)

mkExampleVirtuoso1 :: Effect (ReactComponent {})
mkExampleVirtuoso1 = React.reactComponent "ExampleVirtuoso1" \{} -> React.do
  pure $ React.element virtuosoImpl
    { data: Array.replicate 100_000 "item"
    , itemContent: mkFn3 \i item _ -> R.div { className: "border-b border-b-gray-50 px-5" } (item <> " " <> show (i :: Int))
    , className: "scrollbar scrollbar-thumb-gray-400 hover:scrollbar-thumb-gray-700 scrollbar-track-gray-100 hover:scrollbar-track-gray-200"
    , style: R.css { height: "200px " }
    }
100,000 items with react-virtuoso
src/VirtuosoExample.purs
module VirtuosoExample where

import Prelude
import React.Basic.Hooks as React
import React.Virtuoso (virtuoso)
import React.Basic.DOM (css) as R
import Data.Function.Uncurried (mkFn3)
import React.Basic.DOM.Simplified.Generated (button) as R
import React.Basic.Events (handler_)

mkMyComponent = React.component "InfiniteLoadList" \_props -> React.do
  myData /\ setMyData <- React.useState [ "some", "items" ]
  let addMore = setMyData (_ <> [ "more", "items" ])
  pure $ React.component virtuoso
    { style: R.css { height: "400px" }
    , data: myData
    , context: { addMore } -- Whatever you want
    , itemContent: mkFn3 renderItem
    }
  where
  renderItem index item ctx =
    R.button { onClick: handler_ ctx.addMore }
      [ show index, ": ", item ]