pubkey / rxdb

A fast, local first, reactive Database for JavaScript Applications https://rxdb.info/
https://rxdb.info/
Apache License 2.0
20.94k stars 1.02k forks source link

Feedback: What would be the most optimum way to have a React component re-render whenever a value changes in a document? #6161

Open MrChadMWood opened 3 days ago

MrChadMWood commented 3 days ago

Currently, my approach is to move a state setter function into the subscription with useEffect, and unsubscribe via the effects return function. Like this:

useEffect(() => {
  let subscription;

  const fetchStatus = async () => {
    if (!myCollection || !value.primaryKey) return;

    const document = await myCollection.findById(value.primaryKey).exec();

    // Create a subscription to the document
    subscription = document.$.subscribe( newDoc => {
      // Calculate some derived value
      const newStatus = calculateStatus(
        newDoc.actual_start_date,
        newDoc.actual_finish_date,
        newDoc.expected_start_date,
        newDoc.expected_finish_date
      );
      // Set the new value
      setStatus(newStatus);
    });
  };

  fetchStatus();

  return () => {
    // Unsubscribe once component unmounts
    if (subscription) {
      subscription.unsubscribe();
    }
  };
}, [value.primaryKey, myCollection]);

However, I am noticing some scalability issues with this approach. My use case is to re-render cells within an MUI Data Grid whenever a new "status" is determined for the cell. Statuses change based on user input elsewhere in the application.

I'm utilizing this approach with a very large table, about 500,000 cells with virtualization on both the column and row levels. In my actual use case, a cell corresponds to a "workcenter", which watches 3-6 different documents rather than just one as shown in my example. I must calculate an "overall status" on top of calculating each documents status.

The task is becoming quite expensive and I'm looking to optimize whatever I can. I did not see much documentation specific to React though, so I'd like some feedback. What would be considered the fastest way to make a component re-render based on changing values within a document? Does it make sense to Memoize the document retrieval process, for example?

Thank you.

pubkey commented 3 days ago

rxjs emits/subs can be CPU expensive. So for better performance you can try subscribing to the query instead of each single document. Also use a WeakMap to cache/reuse the result of calculateStatus on a per document basis. Use the _rev of the RxDocument to invalidate that cache.

MrChadMWood commented 2 days ago

I'll give this a try. I see that _rev is a hash of the document. How can I implement its use to invalidate the cache? I looked through the documentation, but wasn't able to find much on the behavior. Thanks!

pubkey commented 2 days ago

_rev is not a hash. It is a string that contains the revision-height and the token of the instance that did the last write. So each time the document gets changed, the revision height is increased by 1. So you can invalidate the cache whenever the revsion string changes because then you know that the document might have changed.