I’m looking for patterns, ideas and howtos on how you use method calls to fetch data, as there are a couple of gotchas (no minimongo etc).
How do YOU fetch and store your data on the client when using Meteor Methods?
I think there are different opinions here based on different front-ends. For React there are mainly two ways: generate a global store with React.createContext or use Redux with actions and reducer.
You action contains your call to the Meteor method, the reducer processes your data and adds it in your preferred shape to your global store. For example, check this log from the browser console, available for Redux. You hava a Previous Stata, the Action with the payload (payload contains your response from the Meteor method or something else) and Next State is where your global store is at after the changes.
You can also use actions to do some level of optimistic UI. For example, you have a button for like and 2 previous likes. You press the button and than this happens:
- You increment the number of likes to 3 by calling a second action to alter your local store.
- You call the method
- You receive the confirmation and the correct number of likes from the Meteor server and send this to your reducer to overwrite your local store.
- You overwrite the number of likes in the view. 99.9999% of the case your server response will be 3 likes :).
Thank you for the explaination and the excellent React-based example ! I can really see how that would be a good path when you don’t need to consider minimongo.
If anyone would have examples for other view-layers, either that be Blaze or Vue or others it would be interesting to see how you utilize Methods for data fetching in those as well.
Just one mention. Redux can be used with any layer. They have a “specialized” react-redux but otherwise Redux can be used with any javascript front end.
@rjakobsson One really easy way to go is to use this package here: GitHub - adtribute/pub-sub-lite: Lighter (Method-based) pub/sub for Meteor. It basically does all the heavy lifting needed to use minimongo with data from methods calls.
I use this with Blaze in a client project and it works really well. The only thing is that (it can be Blaze specific, never checked with React etc) is that when the template is destroyed, I need to manually remove the documents from the client collection. But for me that’s fine, otherwise I would need to manually do everything else the package does.
If you don’t need/want to use minimongo, the simplest way I can think of would be to store the method result in a state variable. Taking React as example:
const DummyComponent = () => {
const [data, setData] = useState([]);
useEffect(() => {
callMethod("methodName", {param1}).then(result => setData(result));
}, [param1]);
return <>{data.length > 0 ? data.map(transformFn) : null}</>
}
It would be something along these lines.
Actually, I think this is a good question to raise as even for a Meteor veteran like me it is unclear what the best path forward is. I heard @fredmaiaarantes say at the conference that he recommends to do all data fetching that does not need to be reactive via a Method call, because it scales better. However, the Meteor Guide says exactly the opposite:
There are some advantages and some disadvantages to this approach compared with loading data through publications, and at the end of the day we recommend always using publications to load data.
I would argue that the data story is one of the main USPs of Meteor, so in my opinion we should do a better job here. It should be crystal clear for developers which option to use in which circumstance, which now evidently things aren’t. I also don’t like the fact that you lose the Minimongo integration when using Methods — why even use Meteor at that point? If I have to manage my client store myself, I might as well use something like Realm Web to get the data straight from MongoDB (they also support real time by the way).
So, a suggestion to make things better. We could add a { reactive: false }
parameter to Meteor.subscribe
to tell the server we only need the data once and don’t care about updates. But a “subscription” that doesn’t update does not make the most semantic sense. So maybe we need a new API for data fetching?
For example:
// Non-reactive, "method call"-like data fetch:
await Meteor.fetch('tasks', {projectId: 'abc'})
// Note that the fetch does not return the data, but adds it to Minimongo:
const data = Tasks.find({projectId: 'abc'})
So my personal recommendation for this is to not use a method and instead seek a well thought out abstraction.
Conveniently this exists specifically for Meteor as the cultofcoders:grapher
package. It’s a little bit of mental effort to wrap your head around at first, and the documentation could be a lot better, but it’s well worth the effort necessary once you fully understand it and can utilize the ability to painlessly and seamlessly switch between reactive and not reactive queries.
+1. Would love to see something like this baked into Meteor rather than having to tack on a package that may or may not be supported or roll my own solution.
I suppose another approach would be obviating the need for fetching via Methods by making it easier to scale pub/sub via @radekmie’s Reflect on the oplog implementation · Discussion #11842 · meteor/meteor · GitHub. But my hunch is that having a better solution for pub/sub and a non-reactive fetch that uses minimongo would be ideal.
Curious to hear others thoughts.
I’m currently exploring the { reactive: false }
- option to Collection.find() etc to see if it can make any difference in my case.
@rijk I think your suggestion regarding the publish option was good.
I do believe it’s strange that there isn’t an official way of doing this and getting the response integrated in minimongo.
You can disable reactive publication by manually adding data to publication. But it’s not very convenient:
Meteor.publish("links.all", function () {
const links = LinksCollection.find();
links.map((link) => {
this.added('links', link._id, link);
});
this.ready();
});
We’ve been using grapher in production for years and it’s really incredible how easy it is to query relational data, both statically and reactively.
If you’re starting from scratch, I would highly recommend using react-query, you will find a few tips here: Principals of writing Meter + React (with hooks) App - #15 by denysk
I built a new package to handle this use case. The data fetched from the Meteor Method will be merged into Minimongo automatically and subsequent db writes will be reactively sent to the client that made the write.
Curious to hear what you think.