Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
f9c6ae3
Expose useTracker hook and reimplement withTracker HOC on top of it
yched Nov 4, 2018
eecab69
run the callback non-reactively in the initial render
yched Nov 5, 2018
46c243a
no need for useCallback
yched Nov 5, 2018
a2cc120
setup computation once on mount, deferring subscriptions until didMount
yched Nov 6, 2018
b61f809
stop deferred inital subscriptions on computation invalidate rather t…
yched Nov 8, 2018
2589ac7
Revert last two commits: no reactivity on mount
yched Nov 9, 2018
c8c1c5f
comments
yched Nov 11, 2018
24be369
minor: code style
yched Feb 15, 2019
e509e26
minor: coding style
yched Mar 30, 2019
5d25bf2
Handle Mongo.Cursor warnings checks inside useTracker, and user React…
yched Mar 30, 2019
ae3f323
Add a note about refs BC break and #262
yched Mar 30, 2019
3caa109
bump React minimum version to 16.8
yched Mar 30, 2019
61ef31e
minor: streamline comments
yched Mar 31, 2019
65e619d
minor: streamline useEffect
yched Mar 31, 2019
3841a4c
minor: streamline client / server versions of useTracker
yched Mar 31, 2019
ef64751
default useTracker's deps param to [] to avoid infinte rerender loop …
yched Mar 31, 2019
4486531
Docs
yched Mar 30, 2019
0041ce7
minor: linebreak fix
yched Mar 31, 2019
92f8576
docs : unify headers for useTracker / withTracker sections (include a…
yched Mar 31, 2019
d8922dd
docs : typos
yched Apr 1, 2019
c4e24f2
remove createContainer / ReactMeteorData
yched Apr 1, 2019
b7a92d6
docs : added compatibility notes
yched Apr 1, 2019
c135ef0
docs : adjust formatting / fix unfinished sentence / link to React li…
yched Apr 1, 2019
e79b596
docs : better doc for the react-hooks/exhaustive-deps ESLint config
yched Apr 3, 2019
17322a5
remove dependency on tmeasday:check-npm-versions
yched Apr 3, 2019
f0a0328
optim : only warn about Mongo.Cursor in dev environment
yched Apr 3, 2019
eb55a16
forward references to the inner component (supercedes #266)
yched Apr 3, 2019
514a87d
fix checkCursor() when reactiveFn returns null
yched Apr 23, 2019
fe4fa42
- rewrite useTracker in order to stay fully consistent with current w…
Jun 21, 2019
fbc33c6
- withTracker should always recompute on re-render so deps for useTra…
Jun 21, 2019
c8d8645
update Readme to reflect omitted deps behavior
Jun 21, 2019
44b5247
fix code comment
Jun 21, 2019
ddfb7cd
get rid of Math.random(), wasn't needed at all
Jun 21, 2019
365584f
don't handle Meteor.isServer as it's already been taken care of in ex…
Jun 21, 2019
c3803b9
replace Math.random() when enforcing an update
Jun 21, 2019
eeb115a
- align areHookInputsEqual with
Jun 22, 2019
1eb71dd
warn if dep is not an array when Meteor isDevelopment
Jun 22, 2019
6b540e6
fix prevDeps isArray check
Jun 22, 2019
8d64e41
warn if initial deps is not an array
Jun 22, 2019
b1996a2
Retain synchronous render behavior for firstRun (including after deps…
CaptainN Jul 1, 2019
af6a4a0
Fix eslint errors
CaptainN Jul 1, 2019
f31b95d
Merge branch 'hooks' into half-duplex
CaptainN Jul 1, 2019
be11569
disambiguate the disposition of previous computation - this works the…
CaptainN Jul 2, 2019
d77fab5
Merge pull request #1 from CaptainN/half-duplex
menelike Jul 4, 2019
bfbf823
Merge branch 'half-duplex' into hooks
CaptainN Jul 4, 2019
80fef10
Use 1 useRef instead of multiple
CaptainN Jul 4, 2019
8ae0db4
If reactiveFn will run synchrously next render, don't bother running …
CaptainN Jul 4, 2019
f68ae5b
Dispose of the computation early, if the deps are falsy on meteor rea…
CaptainN Jul 4, 2019
c587026
previousDeps will always equal deps at this point, so just check prev…
CaptainN Jul 5, 2019
d801348
Merge pull request #3 from CaptainN/hooks
menelike Jul 5, 2019
c9d2e2f
use a self contained forceUpdate value
CaptainN Jul 18, 2019
bb29e32
Rewrite readme for brevity and to update and explain optional `deps`
CaptainN Jul 19, 2019
574416a
Add some basic tests for useTracker
CaptainN Jul 19, 2019
796ce6d
add some after unmount tests
CaptainN Jul 19, 2019
f0df78a
make sure we aren't accidentally triggering reactiveDict's migration …
CaptainN Jul 19, 2019
0c1dd3c
Test changes to enclosed values when not using deps too
CaptainN Jul 19, 2019
68ec9c9
Allow setting deps in withTracker options
CaptainN Jul 20, 2019
7de2ea6
Fix forceUpdate by using a reference to update the counter value
CaptainN Jul 20, 2019
9e59a83
add a comment explaining the use of a reference with forceUpdate and …
CaptainN Jul 20, 2019
bff72c7
Use a much cleaner forceUpdate method based on useReducer
CaptainN Jul 21, 2019
b9d6911
Update to avoid running side effects in render, cause double invocati…
CaptainN Jul 22, 2019
7e71790
Revert "Update to avoid running side effects in render, cause double …
CaptainN Jul 22, 2019
5d73c1a
Make "falsy" deps check in computation consistent with other checks.
CaptainN Jul 22, 2019
044e163
Port the old outdated tests to useTracker (all tests are without deps)
CaptainN Jul 23, 2019
884246c
get more ported tests working
CaptainN Jul 23, 2019
4976a3b
add a version of one of the ported tests with deps
CaptainN Jul 23, 2019
774350c
Add withTracker tests
CaptainN Jul 23, 2019
c3c73f6
Fix some previous stall points with new knowledge from old tests
CaptainN Jul 23, 2019
ee06642
fix typo in computation deps compare
CaptainN Jul 23, 2019
8118837
use es5 functions in package.js
CaptainN Jul 23, 2019
2e786d2
remove remove use of deps in withTracker. It's impractical to use the…
CaptainN Jul 24, 2019
3e29ac5
Clarify some comments and fix the deps check inside the computation.
CaptainN Jul 24, 2019
f472e22
Only warn the user if the dep value is not undefined, null or an array.
CaptainN Jul 24, 2019
cfc2a7e
pass the current computation to reactiveFn
CaptainN Jul 25, 2019
7a9db72
Update readme to include current computation being passed to reactiveFn
CaptainN Jul 25, 2019
092c592
add note about why we are checking deps for null and undefined
CaptainN Jul 25, 2019
4975a2f
Update note about Suspense support.
CaptainN Jul 25, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
113 changes: 94 additions & 19 deletions packages/react-meteor-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,117 @@ npm install --save react

### Usage

This package exports a symbol `withTracker`, which you can use to wrap your components with data returned from Tracker reactive functions.
This package provides two ways to use Tracker reactive data in your React components:
- a hook: `useTracker` (v2 only, requires React `^16.8`)
- a higher-order component (HOC): `withTracker` (v1 and v2).

The `useTracker` hook, introduced in version 2.0.0, embraces the [benefits of hooks](https://reactjs.org/docs/hooks-faq.html). Like all React hooks, it can only be used in function components, not in class components.

The `withTracker` HOC can be used with all components, function or class based.

It is not necessary to rewrite existing applications to use the `useTracker` hook instead of the existing `withTracker` HOC.

#### `useTracker(reactiveFn, deps)` hook

You can use the `useTracker` hook to get the value of a Tracker reactive function in your (function) components. The reactive function will get re-run whenever its reactive inputs change, and the component will re-render with the new value.

Arguments:
- `reactiveFn`: A Tracker reactive function (receives the current computation).
- `deps`: An optional array of "dependencies" of the reactive function. This is very similar to how the `deps` argument for [React's built-in `useEffect`, `useCallback` or `useMemo` hooks](https://reactjs.org/docs/hooks-reference.html) work. If omitted, the Tracker computation will be recreated on every render (Note: `withTracker` has always done this). If provided, the computation will be retained, and reactive updates after the first run will run asynchronously from the react render cycle. This array typically includes all variables from the outer scope "captured" in the closure passed as the 1st argument. For example, the value of a prop used in a subscription or a Minimongo query; see example below.

```js
import { useTracker } from 'meteor/react-meteor-data';

// React function component.
function Foo({ listId }) {
// This computation uses no value from the outer scope,
// and thus does not needs to pass a 'deps' argument.
// However, we can optimize the use of the computation
// by providing an empty deps array. With it, the
// computation will be retained instead of torn down and
// rebuilt on every render. useTracker will produce the
// same results either way.
const currentUser = useTracker(() => Meteor.user(), []);

// The following two computations both depend on the
// listId prop. When deps are specified, the computation
// will be retained.
const listLoading = useTracker(() => {
// Note that this subscription will get cleaned up
// when your component is unmounted or deps change.
const handle = Meteor.subscribe('todoList', listId);
return !handle.ready();
}, [listId]);
const tasks = useTracker(() => Tasks.find({ listId }).fetch(), [listId]);

return (
<h1>Hello {currentUser.username}</h1>
{listLoading ?
<div>Loading</div> :
<div>
Here is the Todo list {listId}:
<ul>{tasks.map(task => <li key={task._id}>{task.label}</li>)}</ul>
</div}
);
}
```

**Note:** the [eslint-plugin-react-hooks](https://www.npmjs.com/package/eslint-plugin-react-hooks) package provides ESLint hints to help detect missing values in the `deps` argument of React built-in hooks. It can be configured to also validate the `deps` argument of the `useTracker` hook or some other hooks, with the following `eslintrc` config:

```
"react-hooks/exhaustive-deps": ["warn", { "additionalHooks": "useTracker|useSomeOtherHook|..." }]
```

#### `withTracker(reactiveFn)` higher-order component

You can use the `withTracker` HOC to wrap your components and pass them additional props values from a Tracker reactive function. The reactive function will get re-run whenever its reactive inputs change, and the wrapped component will re-render with the new values for the additional props.

Arguments:
- `reactiveFn`: a Tracker reactive function, getting the props as a parameter, and returning an object of additional props to pass to the wrapped component.

```js
import { withTracker } from 'meteor/react-meteor-data';

// React component
function Foo({ currentUser, listLoading, tasks }) {
// ...
// React component (function or class).
function Foo({ listId, currentUser, listLoading, tasks }) {
return (
<h1>Hello {currentUser.username}</h1>
{listLoading ?
<div>Loading</div> :
<div>
Here is the Todo list {listId}:
<ul>{tasks.map(task => <li key={task._id}>{task.label}</li>)}</ul>
</div}
);
}

export default withTracker(props => {
// Do all your reactive data access in this method.
export default withTracker(({ listId }) => {
// Do all your reactive data access in this function.
// Note that this subscription will get cleaned up when your component is unmounted
const handle = Meteor.subscribe('todoList', props.id);
const handle = Meteor.subscribe('todoList', listId);

return {
currentUser: Meteor.user(),
listLoading: !handle.ready(),
tasks: Tasks.find({ listId: props.id }).fetch(),
tasks: Tasks.find({ listId }).fetch(),
};
})(Foo);
```
The first argument to `withTracker` is a reactive function that will get re-run whenever its reactive inputs change.

The returned component will, when rendered, render `Foo` (the "lower-order" component) with its provided `props` in addition to the result of the reactive function. So `Foo` will receive `FooContainer`'s `props` as well as `{currentUser, listLoading, tasks}`.
The returned component will, when rendered, render `Foo` (the "lower-order" component) with its provided props in addition to the result of the reactive function. So `Foo` will receive `{ listId }` (provided by its parent) as well as `{ currentUser, listLoading, tasks }` (added by the `withTracker` HOC).

For more information, see the [React article](http://guide.meteor.com/react.html) in the Meteor Guide.

### Note on `withTracker` and `createContainer`
### Version compatibility notes

The new `withTracker` function replaces `createContainer` (however it remains for backwards compatibility). For `createContainer` usage, please [see prior documentation](https://github.com/meteor/react-packages/blob/ac251a6d6c2d0ddc22daad36a7484ef04b11862e/packages/react-meteor-data/README.md). The purpose of the new function is to better allow for container composability. For example when combining Meteor data with Redux and GraphQL:
- `react-meteor-data` v2.x :
- `useTracker` hook + `withTracker` HOC
- Requires React `^16.8`.
- Implementation is **not** compatible with the forthcoming "React Suspense" features. You can use it, but make sure to call `useTracker` *after* any hooks which may throw a Promise. We are looking at ways to improve support for Suspense without sacrificing performance.
- The `withTracker` HOC is strictly backwards-compatible with the one provided in v1.x, the major version number is only motivated by the bump of React version requirement. Provided a compatible React version, existing Meteor apps leveraging the `withTracker` HOC can freely upgrade from v1.x to v2.x, and gain compatibility with future React versions.
- The previously deprecated `createContainer` has been removed.

```js
const FooWithAllTheThings = compose(
connect(...), // some Redux
graphql(...), // some GraphQL
withTracker(...), // some Tracker data
)(Foo);
```
- `react-meteor-data` v1.x / v0.x :
- `withTracker` HOC (+ `createContainer`, kept for backwards compatibility with early v0.x releases)
- Requires React `^15.3` or `^16.0`.
- Implementation relies on React lifecycle methods (`componentWillMount` / `componentWillUpdate`) that are [marked for deprecation in future React versions](https://reactjs.org/blog/2018/03/29/react-v-16-3.html#component-lifecycle-changes) ("React Suspense").
188 changes: 0 additions & 188 deletions packages/react-meteor-data/ReactMeteorData.jsx

This file was deleted.

21 changes: 0 additions & 21 deletions packages/react-meteor-data/createContainer.jsx

This file was deleted.

12 changes: 12 additions & 0 deletions packages/react-meteor-data/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* global Meteor*/
import React from 'react';

if (Meteor.isDevelopment) {
const v = React.version.split('.');
if (v[0] < 16 || v[1] < 8) {
console.warn('react-meteor-data 2.x requires React version >= 16.8.');
}
}

export { default as withTracker } from './withTracker.jsx';
export { default as useTracker } from './useTracker.js';
Loading