UNPKG

@croquet/react

Version:

React bindings for Croquet

50 lines (49 loc) 1.93 kB
import { ReactModel } from '../ReactModel'; import { useEffect, useState } from 'react'; import { useCroquetContext } from './useCroquetContext'; function getModelObject(view, model) { const methods = {}; const excludeMethods = new Set(['view-join', 'view-exit']); if (!view || !model) return null; model.__reactEvents .filter((e) => !excludeMethods.has(e.event)) .forEach(({ scope, event }) => { // Not using usePublish to keep this function pure // @ts-expect-error --- methods[event] = (data) => view.publish(scope, event, data); }); const properties = {}; const excludeProperties = new Set(['__reactEvents']); for (const p in model) { if (!excludeProperties.has(p)) { const prop = model[p]; if (prop instanceof ReactModel) { // @ts-expect-error --- properties[p] = getModelObject(view, prop); } else { properties[p] = prop; } } } return Object.assign(Object.assign({}, properties), methods); } export function useReactModelRoot() { const { session, view, model } = useCroquetContext(); const [modelState, setModelState] = useState(getModelObject(view, model)); useEffect(() => { if (!session || !view || !model) { setModelState(null); return; } // Here we are creating a shallow copy of model to // force react to rerender with the updated data // console.log('@croquet/react: react-updated') const handler = () => setModelState(getModelObject(view, model)); view.subscribe(session.id, { event: 'react-updated', handling: 'oncePerFrame' }, handler); handler(); return () => view.unsubscribe(session.id, 'react-updated', handler); }, [session, view, model, setModelState]); return modelState; }