UNPKG

@croquet/react

Version:

React bindings for Croquet

41 lines (40 loc) 1.75 kB
import { useEffect, useState } from 'react'; import { useCroquetContext } from './useCroquetContext'; import hash_fn from 'object-hash'; export function useModelSelector(selector) { // Storing the first function received in state so that we stick to the first one // const [actualSelector] = useState<(m: M) => R>(selector) const { session, view, model } = useCroquetContext(); // The selector function may return a pointer to an object/array/etc, // Storing the returned value's hash allows to determine if it has changed. // The hash must be computed when the value is set, since // doing it at compare time would result in the same output. const [modelState, setModelState] = useState(() => { if (!model) { return { value: null, hash: null }; } const value = selector(model); return { value, hash: hash_fn(value) }; }); useEffect(() => { if (!session || !view || !model) { // If the previous state was undefined, we return the same object setModelState((prev) => (prev.value === null ? prev : { value: null, hash: null })); return; } const handler = () => { setModelState((prev) => { const newValue = selector(model); const newHash = hash_fn(newValue); return prev.hash === newHash ? prev : { value: newValue, hash: newHash }; }); }; view.subscribe(session.id, { event: 'react-updated', handling: 'oncePerFrame', }, handler); handler(); return () => view.unsubscribe(session.id, 'react-updated', handler); }, [session, view, model]); return modelState.value; }