Skip to content

Commit

Permalink
Voidable sub factory, reactive ready, and a Readme
Browse files Browse the repository at this point in the history
  • Loading branch information
CaptainN committed Nov 3, 2020
1 parent d94b254 commit 98d80d2
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 11 deletions.
40 changes: 40 additions & 0 deletions packages/react-mongo/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
meteor/react-mongo
==================

```
meteor add meteor/react-mongo
```

A set of hooks for using Meteor's Mini Mongo Collections and pub/sub.

There are two hooks

| Hook | Function
| ---- | --------
| useSubscription | Used to set up a Meteor subscription. In SSR, this hook is responsible for capturing data to be sent to the client for hydration.
| useCursor | Manages the lifecycle of a Mongo Cursor

Both methods accept a factory method, and deps.

## useSubscription(factory, deps)

`useSubscription` takes a factory method, which should return a subscription handle, and a deps array. It can also return `void` to conditionally set up no subscription. The hook returns a subscription handle with a reactive `ready` method.

Invoking the `ready()` handle method will cause the hook to update react when the subscription becomes available.

### Example

```jsx
import React from 'react'
import { useSubscription } from 'meteor/react-mongo'

const MyComponent = ({ id = null }) => {
const subscription = useSubscription(() => {
if (id) return Meteor.subscribe(id)
}, [id])

return <div>
{
subscription.ready()
? 'content ready'
: 'loading...'
}
</div>
}
```
41 changes: 31 additions & 10 deletions packages/react-mongo/react-mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,33 +6,54 @@ import { useEffect, useMemo, useReducer, useRef, DependencyList } from 'react'
const fur = (x: number): number => x + 1
const useForceUpdate = () => useReducer(fur, 0)[1]

const useSubscriptionClient = (factory: () => Meteor.SubscriptionHandle, deps: DependencyList = []) => {
const useSubscriptionClient = (factory: () => Meteor.SubscriptionHandle | void, deps: DependencyList= []) => {
const forceUpdate = useForceUpdate()
const subscription = useRef<Meteor.SubscriptionHandle>({
stop() {},
ready: () => false
const { current: refs } = useRef<{
handle?: Meteor.SubscriptionHandle,
updateOnReady: boolean
}>({
handle: {
stop () {
refs.handle?.stop()
},
ready () {
refs.updateOnReady = true
return refs.handle?.ready()
}
},
updateOnReady: false
})

useEffect(() => {
const computation = Tracker.autorun(() => {
subscription.current = factory()
if (subscription.current.ready()) forceUpdate()
})
// Use Tracker.nonreactive in case we are inside a Tracker Computation.
// This can happen if someone calls `ReactDOM.render` inside a Computation.
// In that case, we want to opt out of the normal behavior of nested
// Computations, where if the outer one is invalidated or stopped,
// it stops the inner one.
const computation = Tracker.nonreactive(() => (
Tracker.autorun(() => {
refs.handle = factory()
if (!refs.handle) return
if (refs.updateOnReady && refs.handle.ready()) {
forceUpdate()
}
})
))

return () => {
computation.stop()
}
}, deps)

return subscription.current
return refs.handle
}

const useSubscriptionServer = (): Meteor.SubscriptionHandle => ({
stop() {},
ready() { return true }
})

export const useSubscription = (factory: () => Meteor.SubscriptionHandle, deps: DependencyList = []) => (
export const useSubscription = (factory: () => Meteor.SubscriptionHandle | void, deps: DependencyList = []) => (
Meteor.isServer
? useSubscriptionServer()
: useSubscriptionClient(factory, deps)
Expand Down
7 changes: 6 additions & 1 deletion packages/react-mongo/types/react-mongo.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { Meteor } from 'meteor/meteor';
import { Mongo } from 'meteor/mongo';
import { DependencyList } from 'react';
export declare const useSubscription: (factory: () => Meteor.SubscriptionHandle, deps?: DependencyList) => Meteor.SubscriptionHandle;
declare type UseSubscriptionOptions = {
deps?: DependencyList;
updateOnReady?: boolean;
};
export declare const useSubscription: (factory: () => Meteor.SubscriptionHandle | void, deps?: DependencyList | UseSubscriptionOptions) => void;
export declare const useCursor: <T = any>(factory: () => Mongo.Cursor<T>, deps?: DependencyList) => Mongo.Cursor<T>;
export {};

0 comments on commit 98d80d2

Please sign in to comment.