Hello,
I have quite a few utility functions that I use on both the client and the server, so I’ve migrated them to async. Some of them access collections from functions that are nested a few levels deep. I’ve just discovered that they can no longer be used reactively without wrapping everything in Tracker.withComputation().
This also means that I need to pass the computation into the nested methods, which makes everything quite ugly.
So I just want to check that this is in fact the case and that there isn’t a better way to handle this?
I checked https://v3-migration-docs.meteor.com/ and it doesn’t say anything about Tracker.withComputation() at all.
Sadly the elegant reactivity that you got in Meteor 2 doesn’t seem to be possible in 3?
Thanks,
Graeme
I wrote this little bit of code to just check my understanding. You do need to pass the computation down through each nested function - see getC()
.
Is there perhaps a cleaner way to do this?
import React, {Suspense} from 'react';
import {Mongo} from "meteor/mongo";
import { useTracker } from "meteor/react-meteor-data/suspense";
import { Tracker } from "meteor/tracker";
export const ACollection = new Mongo.Collection(null);
export const BCollection = new Mongo.Collection(null);
export const CCollection = new Mongo.Collection(null);
global.ACollection = ACollection;
global.BCollection = BCollection;
global.CCollection = CCollection;
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function concatIds(col) {
return (await col.find().fetchAsync()).reduce((acc, cur) => acc + cur._id, "");
}
async function getFlatABC(comp) {
// await sleep(1);
const a = await concatIds(ACollection);
const b = await Tracker.withComputation(comp, () => concatIds(BCollection));
const c = await Tracker.withComputation(comp, () => concatIds(CCollection));
return a + b + c;
}
async function getABC(comp) {
// await sleep(1);
const a = await concatIds(ACollection);
const bc = await Tracker.withComputation(comp, () => getBC(comp));
// const bc = await getBC(comp);
return a + bc;
}
async function getBC(comp) {
const b = await concatIds(BCollection);
const c = await Tracker.withComputation(comp, () => getC(comp));
// const c = await getC(comp);
return b + c;
}
async function getC(comp) {
const c = await concatIds(CCollection);
return c;
}
export const App = () => {
const abc = useTracker("abc", async comp => await getABC(comp));
return <div>
<Suspense fallback={"loading"}>abc: {abc}</Suspense>
</div>
};
Hey @graemian
I feel your pain. For this very reason our plan (as yet unexecuted) is to simply divide up isomorphic code so we leave the client untouched (well, as untouched as we can) and only go async on the server where it’s required. At a very crude level you could just make a copy of your utility functions - one into /client and one into /server - then update the server side version to async.
Another more elaborate way might be to refactor your isomorphic code to take the data it needs as parameters so you avoid the collection api in it. Although this is more work and maybe not always possible.