Working with Data
The Message List component exposes an imperative API to interact with the list data. This API is available through the useVirtuosoMethods
hook from components nested inside and through the ref
object you can pass into the component.
Prepending Data
Use this method when you need to add additional messages before the current ones, for example when loading older messages. The component will automatically adjust the scroll position to keep the previous messages in view.
function App() { const ref = React.useRef<VirtuosoMessageListMethods>(null); const offset = React.useRef(0); return ( <> <button onClick={() => { offset.current = offset.current - 10; ref.current.data.prepend( Array.from({ length: 10 }, (_, index) => index + offset.current), ); }} > Prepend 10 items </button> <VirtuosoMessageListLicense licenseKey=""> <VirtuosoMessageList ItemContent={({ data }) => <div>{data}</div>} ref={ref} style={{ height: 400 }} initialData={Array.from({ length: 100 }, (_, index) => index)} /> </VirtuosoMessageListLicense> </> ); }
Appending data
Appending data to the message list usually requires an adjustment to the scroll position to keep the new messages in view. However, this is not always the case - for example, if the user is scrolling up to read older messages. Also, depending on various factors the adjustment may happen instantly or with a smooth scroll animation.
To support those scenarios, the data.append
method accepts data
and, an optional, scrollToBottom
argument that can be a string - "smooth"
, "auto"
or "instant"
(passed directly to the underlying scrollTo
DOM call), or a function that receives the following arguments as an object:
scrollLocation: ListScrollLocation
- the current scroll location (same object as the one passed to theonScroll
callback and the one available fromuseScrollLocation
hook)scrollInProgress: boolean
- whether or not the list is currently scrollingatBottom: boolean
- whether the list is scrolled to the bottom before the new items are appendeddata: Data[]
- the data that was appendedcontext: Context
- the context object passed to the list
The function should return:
- a boolean value,
false
meaning that no scroll should happen,true
meaning that the list should scroll to the bottom instantly - a string value, one of
"smooth"
,"auto"
or"instant"
to control the scroll behavior - the list will scroll to the bottom with the specified behavior - a location object,
{ index: number | 'LAST', align: 'start' | 'center' | 'end', behavior: 'smooth' | 'auto' }
to control the scroll behavior and the alignment of the new items. You can use this to scroll to the bottom, or to scroll the first new message to the top of the list.
Experiment with the live example below to see how the scroll behavior changes when appending new items.
function App() { const ref = React.useRef<VirtuosoMessageListMethods>(null); const offset = React.useRef(100); return ( <> <VirtuosoMessageListLicense licenseKey=""> <VirtuosoMessageList ItemContent={({ data }) => <div>{data}</div>} ref={ref} style={{ height: 400 }} initialData={Array.from({ length: 100 }, (_, index) => index)} /> </VirtuosoMessageListLicense> <button onClick={() => { ref.current.data.append( Array.from({ length: 10 }, (_, index) => index + offset.current), (params) => true, ); offset.current = offset.current + 10; }} > Append 10 items </button> </> ); }
Updating Data
It is highly recommended that, in addition to the message content itself, you also keep any necessary state in the objects present in data. This includes things like message reactions, read status, but also ephemeral state like whether the message has been expanded to display additional details, for example. Keeping this information there makes it easy to restore the message look when it has been scrolled out of the viewport and then back in. Also, using the data.map
function lets the list readjust its scroll to bottom location if the updates increase the size of the message element. A common use case for this is displaying reactions to messages.
The data.map
method accepts the following arguments:
callbackfn: (item: Data) => Data
- a function that takes the current data item and returns the updated data item. The function should return the same object if no changes are needed.autoscrollToBottomBehavior?: 'smooth' | 'auto'
- an optional field that lets you specify the necessary behavior if the data mapping causes the list to be no longer at the bottom.
To see a data.map
example usage, visit the Reactions example. The tutorial also uses data.map
for the optimistic rendering of sent messages.
Removing Data
The data.findAndDelete(predicate: (item: Data) => boolean)
method lets you remove a message from the list. The predicate function receives the data item and should return a boolean value indicating whether the item should be removed. The example below adds a button next to each item that removes it.
function ItemContent({ data }) { const virtuosoMethods = useVirtuosoMethods(); return ( <div> Item {data} <button onClick={() => virtuosoMethods.data.findAndDelete((item) => item === data) } > Remove </button> </div> ); } function App() { return ( <VirtuosoMessageListLicense licenseKey=""> <VirtuosoMessageList ItemContent={ItemContent} style={{ height: 400 }} initialData={Array.from({ length: 100 }, (_, index) => index)} /> </VirtuosoMessageListLicense> ); } render(<App />);
You can remove multiple items at once with the data.deleteRange
method.
function ItemContent({ data }) { const virtuosoMethods = useVirtuosoMethods(); return <div>Item {data}</div>; } function App() { const ref = useRef<VirtuosoMessageListMethods>(null); return ( <> <button onClick={() => ref.current.data.deleteRange(5, 10)}> Remove 10 items starting from 5 </button> <VirtuosoMessageListLicense licenseKey=""> <VirtuosoMessageList ref={ref} ItemContent={ItemContent} style={{ height: 400 }} initialData={Array.from({ length: 100 }, (_, index) => index)} /> </VirtuosoMessageListLicense> </> ); } render(<App />);
Replacing Data
In case you're building a chat application with multiple channels, you might want to switch the message list to a different channel. The data.replace
method lets you replace the current list of messages with a new one.
The method accepts data
and, optionally, an options: { initalLocation: ItemLocation, purgeItemSizes?: boolean }
to specify the initial scroll position and weather to clear the cached item sizes. Purging the item sizes is useful when the new data has different item sizes than the previous data.
function App() { const ref = React.useRef<VirtuosoMessageListMethods>(null); return ( <> <button onClick={() => { const newData = Array.from( { length: 100 }, (_, index) => index + 100, ); ref.current.data.replace(newData, { initialLocation: { index: "LAST", align: "end" }, purgeItemSizes: true, }); }} > Replace with 100 items (100-200) </button> <VirtuosoMessageListLicense licenseKey=""> <VirtuosoMessageList ItemContent={({ data }) => <div>{data}</div>} ref={ref} initialLocation={{ index: "LAST", align: "end" }} style={{ height: 400 }} initialData={Array.from({ length: 100 }, (_, index) => index)} /> </VirtuosoMessageListLicense> </> ); }
Querying Data
To retrieve records from the current data set, you can use find
and findIndex
methods, which work like their array counterparts.
You can also access the entire data set through the get
method, which returns a shallow copy of the data.
The subset of data that is currently rendered can be accessed through the getCurrentlyRendered
method.