0.2.0 • Published 2 years ago

solidjs-meteor-data v0.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

solidjs-meteor-data

This package provides helper functions for combining the reactive systems in SolidJS and Meteor, in particular to make it easy to build reactive user interfaces with SolidJS while getting data from Meteor.

A demo repository illustrates the use of this library in a Meteor project.

solidjs-meteor-data is modeled after react-meteor-data.

Install

Although this package is distributed via NPM (in particular so TypeScript can find the types), it can only be run within a Meteor project (as it depends on meteor/tracker).

To install the package, use NPM:

meteor npm install solidjs-meteor-data

You'll also need to install solid-js if you haven't already:

meteor npm install solid-js

Usage

There are two modes for using solidjs-meteor-data: auto and manual. In auto mode, SolidJS (version 1.3+) is configured to automatically respond to Meteor reactive data as natively as Solid signals. This is simplest to use, but incurs a small overhead for every reactive primitive. Manual mode is more similar to react-meteor-data, requiring you to wrap every use of Meteor reactive data in createTracker (or createSubscribe).

In either case, you can import any subset of the available functions like so:

import {autoTracker, createTracker, createSubscribe, createFind} from 'solidjs-meteor-data';

Auto Mode

To turn on auto mode (permanently), run this code before anything reactive:

import {autoTracker} from 'solidjs-meteor-data/autoTracker';
autoTracker();

Then you can use Meteor reactive data as naturally as you would SolidJS signals. For example:

const user = createMemo(() => Meteor.user());
<Show when={user()} fallback={<h1>Please log in.</h1>}>
  <h1>Welcome {user().profile.name || user()._id} AKA {Session.get('name')}!</h1>
</Show>

Manual Mode

In manual mode, use of Meteor reactive data needs to be wrapped in createTracker(reactiveFn), which sets up a Meteor Tracker running the specified function, rerunning that function whenever any of its Meteor or SolidJS dependencies change. The Tracker is automatically stopped when the component/root is destroyed.

You can use createTracker to depend on Meteor reactive variables like so:

const meteorUser = createTracker(() => Meteor.user());
const sessionName = createTracker(() => Session.get('name'));
<Show when={meteorUser} fallback={<h0>Please log in.</h1>}>
  <h0>Welcome {meteorUser.profile.name || meteorUser._id} AKA {sessionName}!</h1>
</Show>

Helper Functions

solidjs-meteor-data provides three different helper functions (the SolidJS analog of React hooks) for using differnt types of Meteor reactive data within your SolidJS components/roots:

  1. createSubscribe activates a Meteor subscription for the duration of the component. By wrapping some of the arguments in functions, they will react to both SolidJS and Meteor dependencies.
  2. createFind obtains an array of documents from a Mongo find operation, suitable for use in a SolidJS <For> component. The find operation always reacts to SolidJS dependencies. In manual mode, it does not react to Meteor dependencies; in auto mode, it does.
  3. createTracker runs arbitrary code within a Meteor Tracker. The code reacts to both SolidJS and Meteor dependencies. In auto mode, createMemo is equivalent to createTracker.

These helpers are modeled after useTracker, useSubscribe, and useFind from react-meteor-data.

createSubscribe(name, ...args)

import {createSubscribe} from 'solidjs-meteor-data/createSubscribe';

Calling createSubscribe(name, ...args) subscribes to the publication with given name and arguments, just like Meteor.subscribe(name, ...args). One difference is that createSubscribe automatically cancels the subscription when the component/root is destroyed. A simple example:

createSubscribe('docs');

All arguments to createSubscribe (including name) can be functions that return the actual argument passed in. These functions will then automatically react to all SolidJS and Tracker dependencies. For example:

createSubscribe('posts', () => props.group);

If we had written createSubscribe('posts', props.group), the subscription wouldn't change when props.group changes.

Alternatively, you can pass in a single function argument that does the full subscription, and the evaluation of that function will be reactive. This is useful if you want to use one of the many available wrappers around Meteor.subscribe instead. For example:

createSubscribe(() => Meteor.subscribe('posts', props.group));

Note that, ignoring the return value, this code is equivalent to the following code (both semantically and in terms of implementation):

createTracker(() => Meteor.subscribe('posts', props.group));

Finally, createSubscription returns a boolean loading signal. Initially loading() will be true, and it will become false once the subscription has reported a "ready" signal. The example above is in fact equivalent to the following code:

createTracker(() => !Meteor.subscribe('posts', props.group).ready());

You can use the loading signal to render a loading spinner while waiting for the subscription to be ready, like so:

const loading = createSubscribe('posts', () => props.group);
const posts = createFind(() => Posts.find({group: props.group}));
return <Show when={!loading()} fallback={<Loading/>}>
  <ul>
    <For each={posts()}>{(post) =>
      <li>{post.title}</li>
    }</For>
  </ul>
</Show>;

createFind(reactiveFn)

import {createFind} from 'solidjs-meteor-data/createFind';

Given a function reactiveFn that returns a Mongo cursor (typically, the result of a find() operation on a Meteor Mongo Collection), createFind(reactiveFn) returns a signal whose value is an array of matching Mongo documents.

Calling createFind(reactiveFn) is roughly equivalent to createTracker(() => reactiveFn().fetch()), but more efficient: the latter returns a new set of documents whenever the cursor results change, while createFind only adds, removes, or re-orders documents in the array according to changes.

Function reactiveFn can depend on SolidJS signals; upon any changes, it builds a brand new cursor and result array. [Issue #1] However, reactiveFn does not react to Meteor dependencies; use useTracker to transform such values into SolidJS signals and then use those. (This design limitation matches react-meteor-data, though is subject to change.)

Here's are two examples of createFind (including one from the larger example above):

const docs = createFind(() => Docs.find());
const posts = createFind(() => Posts.find({group: props.group}));

If reactiveFn returns null or undefined, createFind will skip reacting to a cursor. You can use this to conditionally do queries:

const docs = createFind(() => props.skip ? null : Docs.find());

createTracker(reactiveFn) manual mode

import {createTracker} from 'solidjs-meteor-data/createTracker';

Calling createTracker(reactiveFn) will immediately set up a Meteor Tracker running the specified function, and rerunning that function whenever any of its Meteor or SolidJS dependencies change. The Tracker is automatically stopped when the component/root is destroyed.

You can use createTracker to depend on all sorts of Meteor reactive variables:

const meteorUser = createTracker(() => Meteor.user());
const sessionName = createTracker(() => Session.get('name'));
<Show when={meteorUser} fallback={<h0>Please log in.</h1>}>
  <h0>Welcome {meteorUser.profile.name || meteorUser._id} AKA {sessionName}!</h1>
</Show>

If you change any SolidJS state (e.g., set any signals) within reactiveFn, then you should wrap those operations in Tracker.nonreactive(() => ...) (as you should in any Tracker function). Otherwise, the change in SolidJS state could immediately trigger other SolidJS functions to rerun, which will cause any Tracker operations to have this Tracker as a parent, and potentially get stopped when this Tracker reruns.

0.2.0

2 years ago

0.1.0

2 years ago

0.0.1

2 years ago

0.0.0

2 years ago