@multisynq/react
Version:
React bindings for Multisynq
73 lines (62 loc) • 2.01 kB
text/typescript
import { useEffect, useState } from 'react'
import { ReactModel, type ViewInfo } from '../ReactModel'
import { useMultisynqContext } from './useMultisynqContext'
interface JoinedViews<T> {
viewIds: string[]
viewInfos: { [viewId: string]: ViewInfo<T> }
viewCount: number
// Deprecated
views: string[]
}
// Make sure we only warn once
let warned = false
function deprecatedViewsPropertyWarning() {
if (warned) return
warned = true
console.warn('Accessing the `views` property of `useJoinedViews` is deprecated. Use `viewIds` instead.')
}
const EMPTY_ARRAY: string[] = []
const EMPTY_OBJECT = {}
const NO_VIEWS = {
viewIds: EMPTY_ARRAY,
viewInfos: EMPTY_OBJECT,
viewCount: 0,
get views() {
deprecatedViewsPropertyWarning()
return this.viewIds
}
}
function viewsSelector<T>(rootModel: ReactModel<T> | null): JoinedViews<T> {
if(!rootModel?.__views) return NO_VIEWS as JoinedViews<T>
const views = rootModel.__views
return {
viewIds: Array.from(views.keys()),
viewInfos: Object.fromEntries(views.entries()),
viewCount: views.size,
get views() {
deprecatedViewsPropertyWarning()
return this.viewIds
}
}
}
export function useJoinedViews<T = undefined>(): JoinedViews<T> {
const { session, view, model } = useMultisynqContext<ReactModel<T>>()
const [views, setViews] = useState(viewsSelector<T>(model))
useEffect(() => {
if (session && view && model) {
const handler = () => setViews(viewsSelector<T>(model))
view.subscribe(session.id, {event: 'views-updated', handling: 'oncePerFrame'}, handler)
handler()
return () => view.unsubscribe(session.id, 'views-updated', handler)
} else {
setViews(NO_VIEWS)
}
}, [session, view, model])
if (model && !model.__views) {
throw new Error(
'Your root model is not tracking the joined views.\n' +
'Pass `options: { trackViews: true }` to your <MultisynqRoot> component to start tracking them\n'
)
}
return views
}