Skip to content

Commit 07f607c

Browse files
authored
Merge pull request #332 from meteor/useFind
useFind and useSubscribe
2 parents 62c9f12 + 472d5ba commit 07f607c

File tree

14 files changed

+578
-85
lines changed

14 files changed

+578
-85
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
package.json
3+
package-lock-json

packages/react-meteor-data/.versions

Lines changed: 31 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,56 @@
11
2-
babel-compiler@7.6.1
2+
babel-compiler@7.7.0
33
44
55
6-
blaze@2.3.4
6+
blaze@2.5.0
77
8-
callback-hook@1.3.0
8+
callback-hook@1.4.0
99
1010
11-
ddp-client@2.4.0
11+
ddp-client@2.5.0
1212
13-
14-
13+
1514
16-
dynamic-import@0.6.0
17-
ecmascript@0.15.1
18-
ecmascript-runtime@0.7.0
19-
ecmascript-runtime-client@0.11.0
20-
ecmascript-runtime-server@0.10.0
15+
dynamic-import@0.7.2
16+
ecmascript@0.16.0
17+
ecmascript-runtime@0.8.0
18+
ecmascript-runtime-client@0.12.1
19+
ecmascript-runtime-server@0.11.0
2120
2221
2322
24-
htmljs@1.0.11
25-
23+
htmljs@1.1.1
24+
2625
27-
local-test:react-meteor-data@2.3.3
28-
logging@1.2.0
29-
meteor@1.9.3
30-
minimongo@1.6.2
31-
32-
modules@0.16.0
26+
local-test:react-meteor-data@2.4.0
27+
logging@1.3.1
28+
meteor@1.10.0
29+
minimongo@1.7.0
30+
31+
modules@0.17.0
3332
34-
mongo@1.11.0
33+
mongo@1.13.0
3534
3635
37-
38-
39-
36+
37+
38+
4039
41-
promise@0.11.2
40+
promise@0.12.0
4241
43-
react-fast-refresh@0.1.0
44-
react-meteor-data@2.3.3
42+
react-fast-refresh@0.2.0
43+
react-meteor-data@2.4.0
4544
4645
4746
4847
49-
50-
socket-stream-client@0.3.1
51-
test-helpers@1.2.0
52-
tinytest@1.1.0
48+
49+
socket-stream-client@0.4.0
50+
test-helpers@1.3.0
51+
tinytest@1.2.0
5352
54-
typescript@4.2.2
53+
typescript@4.4.0
5554
56-
webapp@1.10.1
55+
webapp@1.13.0
5756

packages/react-meteor-data/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
# CHANGELOG
2+
## v2.4.0, 2021-12-02
3+
* Added `useSubscribe` and `useFind` hooks
4+
25
## v2.3.3, 2021-07-14
36
* Fixes a publication issue in v2.3.2
47

packages/react-meteor-data/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,79 @@ export default withTracker({
231231
})(Foo);
232232
```
233233
234+
#### `useSubscribe(subName, ...args)` A convenient wrapper for subscriptions
235+
236+
`useSubscribe` is a convenient short hand for setting up a subscription. It is particularly useful when working with `useFind`, which should NOT be used for setting up subscriptions. At its core, it is a very simple wrapper around `useTracker` (with no deps) to create the subscription in a safe way, and allows you to avoid some of the cerimony around defining a factory and defining deps. Just pass the name of your subscription, and your arguments.
237+
238+
`useSubscribe` returns an `isLoading` function. You can call `isLoading()` to react to changes in the subscription's loading state. The `isLoading` function will both return the loading state of the subscription, and set up a reactivity for the loading state change. If you don't call this function, no re-render will occur when the loading state changes.
239+
240+
```jsx
241+
// Note: isLoading is a function!
242+
const isLoading = useSubscribe('posts', groupId);
243+
const posts = useFind(() => Posts.find({ groupId }), [groupId]);
244+
245+
if (isLoading()) {
246+
return <Loading />
247+
} else {
248+
return <ul>
249+
{posts.map(post => <li key={post._id}>{post.title}</li>)}
250+
</ul>
251+
}
252+
```
253+
254+
If you want to conditionally subscribe, you can set the `name` field (the first argument) to a falsy value to bypass the subscription.
255+
256+
```jsx
257+
const needsData = false;
258+
const isLoading = useSubscribe(needsData ? "my-pub" : null);
259+
260+
// When a subscription is not used, isLoading() will always return false
261+
```
262+
263+
#### `useFind(cursorFactory, deps)` Accellerate your lists
264+
265+
The `useFind` hook can substantially speed up the rendering (and rerendering) of lists coming from mongo queries (subscriptions). It does this by controlling document object references. By providing a highly tailored cursor management within the hook, using the `Cursor.observe` API, `useFind` carefully updates only the object references changed during a DDP update. This approach allows a tighter use of core React tools and philosophies to turbo charge your list renders. It is a very different approach from the more general purpose `useTracker`, and it requires a bit more set up. A notable difference is that you should NOT call `.fetch()`. `useFind` requires its factory to return a `Mongo.Cursor` object. You may also return `null`, if you want to conditionally set up the Cursor.
266+
267+
Here is an example in code:
268+
269+
```jsx
270+
import React, { memo } from 'react'
271+
import { useFind } from 'meteor/react-meteor-data'
272+
import TestDocs from '/imports/api/collections/TestDocs'
273+
274+
// Memoize the list item
275+
const ListItem = memo(({doc}) => {
276+
return (
277+
<li>{doc.id},{doc.updated}</li>
278+
)
279+
})
280+
281+
const Test = () => {
282+
const docs = useFind(() => TestDocs.find(), [])
283+
return (
284+
<ul>
285+
{docs.map(doc =>
286+
<ListItem key={doc.id} doc={doc} />
287+
)}
288+
</ul>
289+
)
290+
}
291+
292+
// Later on, update a single document - notice only that single component is updated in the DOM
293+
TestDocs.update({ id: 2 }, { $inc: { someProp: 1 } })
294+
```
295+
296+
If you want to conditionally call the find method based on some props configuration or anything else, return `null` from the factory.
297+
298+
```jsx
299+
const docs = useFind(() => {
300+
if (props.skip) {
301+
return null
302+
}
303+
return TestDocs.find()
304+
}, [])
305+
```
306+
234307
### Concurrent Mode, Suspense and Error Boundaries
235308
236309
There are some additional considerations to keep in mind when using Concurrent Mode, Suspense and Error Boundaries, as each of these can cause React to cancel and discard (toss) a render, including the result of the first run of your reactive function. One of the things React developers often stress is that we should not create "side-effects" directly in the render method or in functional components. There are a number of good reasons for this, including allowing the React runtime to cancel renders. Limiting the use of side-effects allows features such as concurrent mode, suspense and error boundaries to work deterministically, without leaking memory or creating rogue processes. Care should be taken to avoid side effects in your reactive function for these reasons. (Note: this caution does not apply to Meteor specific side-effects like subscriptions, since those will be automatically cleaned up when `useTracker`'s computation is disposed.)

packages/react-meteor-data/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,5 @@ if (Meteor.isDevelopment) {
1010

1111
export { default as useTracker } from './useTracker';
1212
export { default as withTracker } from './withTracker.tsx';
13+
export { useFind } from './useFind';
14+
export { useSubscribe } from './useSubscribe';

0 commit comments

Comments
 (0)