Meteor.call inside withTracker()

Hi guys,

I guess many of you already were in the same situation, that you need to load data not only from subscriptions, but also via Meteor.call. A very elegant way would be to have all this data-fetching in one place in the withTracker HOC.

I have fiddled around with it but it seems to always break the component - one big problem is the loading prop, which I usually define as const loading = !sub.ready() - has anyone found a solution to also use Meteor.call inside the withTracker HOC?

Solving it via componentDidUpdate() is very tedious because of needing to check if the props have changed and all that stuff.

Thanks, cheers
Patrick

I solved this with using state. Once the data is retrieved I push it into state.

You should not use Meteor.call inside a computation, because it will get invoked every single time the computation updates, which can happen at indeterminate intervals, and possibly even every time the component renders (if you don’t supply deps).

Instead, if you want to keep things coupled, you could compose your hooks, into another hook:

const useMyCustomHook = (query) => {
  // useTracker first if we are using that data for our method call
  const trackerData = useTracker(() => {
    const sub = Meteor.subscribe('my-pub', query)
    const data = MySubCollection.find({}).fetch()
    return {
      data,
      isLoading: !sub.ready()
    }
  }, [query]) // set deps as appropriate (or not, it's not required)

  //  state for your method
  const [methodData, updateMethodData] = useState(null)

  // call method in useEffect
  useEffect(() => {
    // if we don't have the necessary data, just return void or null
    if (trackerData.isLoading) return null

    // if we are loaded, but don't have expected data, throw
    if (!trackerData.data[0]?.whatever) throw new Error('whatever')

    // call the method with data from the trackerData
    Meteor.call('some-method', trackerData.data[0].whatever, (err, res) =>  {
      if (err) {
         console.error(err) // or throw, or whatever
         return
      }

      // update the methodData state, and re-render
      updateMethodData(res)
    })

  }, [trackerData.data.isLoading, trackerData.data[0].whatever) // set deps as appropriate (required) - if we rely on data from tracker, we pass that here

  return {
    methodData,
    trackerData
  }
}

Now you can call useMyCustomHook(query) wherever you need this set of data. This sort of functional composition is what makes hooks so grand.

2 Likes