TarVK / model-react

A data model system to use with react hooks
https://tarvk.github.io/model-react/examples/build/
MIT License
13 stars 3 forks source link

Add persistent hook from `useDataHook` #40

Closed TarVK closed 3 years ago

TarVK commented 3 years ago

Currently data hooks obtained from useDataHook are always cleared when the element rerenders. In some scenarios this isn't desirable however. Consider the following scenario:

const [time, setTime] = useState(0);
useEffect(()=>{
  const id = setInterval(()=>setTime(t=>t+1), 1000);
  return ()=>clearInterval(id);
});

const version = useRef(0);
const [h] = useDataHook({onChange: ()=>version.current++});
const someData = useMemo(()=>computeSomethingExpensive(h), [version.current]);

Here the clock at the top will cause multiple rerenders, which make sure that h is unregistered continuously. But only in some cases will it be reregistered (namely if the expensive computation caused the rerender). In this situation we would prefer the hook not to unregister. Ideally, we would want the component to rerender whenever the expensive computation indicates a change is needed, but we would not want to perform the expensive computation on every rerender.

Overall unregistering of the hook makes for intuitive behavior however, such as in this scenario:

const [enabled, setEnabled] = useState(false);
const [h] = useDataHook();

const val = enabled ? getVal1(h) : getVal2(h);

You wouldn't want to stay dependent on val2 when enabled was changed to true, and more importantly you wouldn't want hook dependencies to build up in the getters for val1 and val2.

So maybe a specialized useMemoHook makes more sense, which could be used as:

const [someData] = useMemoHook((h)=>computeSomethingExpensive(h));

We could also implement this relatively easily using the existing dataCacher source:

function useMemoHook(dataRetriever, {dependencies, ...rest}) {
  const cacher = useMemo(()=>new DataCacher(dataRetriever), dependencies);
  const [h, ...d] = useDataHook(rest);
  return [cacher.get(h), ...d];
}