Meteor React Collection Hooks

How to use Meteor collections pub/sub with react hooks: clean wrapping of meteor subscription data with the useTracker hook.


Let’s start with a simple collection (for example: /imports/api/articles/index.ts)

import { Mongo } from 'meteor/mongo';

export type Article = {
  slug: string,
  title: string,
  description: string,
  text: string,
}

export const ArticlesCollection = new Mongo.Collection<Article>('articles');

// security best practice: deny all client-side updates by default
ArticlesCollection.deny({
  insert() { return true; },
  update() { return true; },
  remove() { return true; },
});

Now define some server-side publications (for example: /imports/api/articles/server/publications.ts).

import { Meteor } from "meteor/meteor";
import { ArticlesCollection } from "..";

Meteor.publish('articles', () => {
  return ArticlesCollection.find({})
});

Meteor.publish('article', ({ slug }) => {
  return ArticlesCollection.find({ slug })
});

Remember that you have to include this in the actual server code, like in /server/main.ts:

import '../imports/api/shared/articles/server/publications';

Now to the interesting part, let’s define two hooks to actually fetch the data (for example: /imports/api/articles/client/hooks.tsx)

import { Meteor } from "meteor/meteor";
import { useTracker } from "meteor/react-meteor-data";
import { Article, ArticlesCollection } from "..";

export const useArticle = (slug: string): Article | undefined => {
  return useTracker(() => {
    const sub = Meteor.subscribe('article', { slug });
    if (sub.ready()) {
      return ArticlesCollection.findOne({ slug })
    } else {
      return undefined
    }
  }, [slug])
}

export const useArticles = (): Article[] => {
  return useTracker(() => {
    const sub = Meteor.subscribe('articles');
    if (sub.ready()) {
      return ArticlesCollection.find().fetch()
    } else {
      return []
    }
  })
}

Note that the last argument of the useTracker hook (similar to useEffect) does declare a reactive dependency on the slug in the first hook. It may be undefined on the first run (like, when you fetch the slug from react-router with a hook first).

Now rendering some react pages is as easy as this example:

import React from 'react';
import { useParams } from "react-router-dom";
import { useArticle } from '/imports/api/articles/client/hooks';
import { Template } from '/imports/ui/templates/ArticleTemplate';

export default () => {
  const { slug } = useParams<{ slug: string }>();
  const article = useArticle(slug);
  if (article) {
    return (<Template article={article} />)
  } else {
    return (<div>loading data...</div>)
  }
};

Linked Technologies

What it's made of

illustration of Javascript
Javascript

Power up your web projects! Essential for dynamic interactions and seamless user experiences. A must-have for every developer's toolkit.

illustration of Meteor
Meteor

Streamline app development with this full-stack platform. Rapid prototyping, real-time updates, and integrated tools make it ideal for startups.

illustration of React
React

React.js: Build dynamic and responsive UIs with ease. Perfect for indie hackers looking to create scalable, efficient web applications!

illustration of Typescript
Typescript

Supercharge your JavaScript with strong typing. Ensure more reliable code and robust applications with this scalable, developer-friendly language.

Linked Categories

Where it's useful

illustration of Software Architecture
Software Architecture

Dive into the world of Software Architecture where design meets functionality. Explore frameworks, patterns, and best practices that define the structure of software systems, making them robust, scalable, and maintainable.