@croquet/react
Version:
React bindings for Croquet
41 lines (40 loc) • 1.75 kB
JavaScript
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;
}