Skip to content
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
28 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
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
2 changes: 2 additions & 0 deletions packages/react-meteor-data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ npm install --save react

### Usage

@TODO document `useTracker` hook

This package exports a symbol `withTracker`, which you can use to wrap your components with data returned from Tracker reactive functions.

```js
Expand Down
30 changes: 1 addition & 29 deletions packages/react-meteor-data/ReactMeteorData.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ class MeteorDataManager {
}
}

export const ReactMeteorData = {
export default ReactMeteorData = {
componentWillMount() {
this.data = {};
this._meteorDataManager = new MeteorDataManager(this);
Expand Down Expand Up @@ -158,31 +158,3 @@ export const ReactMeteorData = {
this._meteorDataManager.dispose();
},
};

class ReactComponent extends React.Component {}
Object.assign(ReactComponent.prototype, ReactMeteorData);
class ReactPureComponent extends React.PureComponent {}
Object.assign(ReactPureComponent.prototype, ReactMeteorData);

export default function connect(options) {
let expandedOptions = options;
if (typeof options === 'function') {
expandedOptions = {
getMeteorData: options,
};
}

const { getMeteorData, pure = true } = expandedOptions;

const BaseComponent = pure ? ReactPureComponent : ReactComponent;
return (WrappedComponent) => (
class ReactMeteorDataComponent extends BaseComponent {
getMeteorData() {
return getMeteorData(this.props);
}
render() {
return <WrappedComponent {...this.props} {...this.data} />;
}
}
);
}
4 changes: 2 additions & 2 deletions packages/react-meteor-data/createContainer.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import { Meteor } from 'meteor/meteor';
import React from 'react';
import connect from './ReactMeteorData.jsx';
import withTracker from './withTracker.jsx';

let hasDisplayedWarning = false;

Expand All @@ -17,5 +17,5 @@ export default function createContainer(options, Component) {
hasDisplayedWarning = true;
}

return connect(options)(Component);
return withTracker(options)(Component);
}
5 changes: 3 additions & 2 deletions packages/react-meteor-data/react-meteor-data.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ checkNpmVersions({
}, 'react-meteor-data');

export { default as createContainer } from './createContainer.jsx';
export { default as withTracker } from './ReactMeteorData.jsx';
export { ReactMeteorData } from './ReactMeteorData.jsx';
export { default as ReactMeteorData } from './ReactMeteorData.jsx';
export { default as withTracker } from './withTracker.jsx';
export { default as useTracker } from './useTracker.js';
55 changes: 55 additions & 0 deletions packages/react-meteor-data/useTracker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { useState, useEffect } from 'react';
import { Tracker } from 'meteor/tracker';
import { Meteor } from 'meteor/meteor';

let useTracker;

if (Meteor.isServer) {
// When rendering on the server, we don't want to use the Tracker.
// We only do the first rendering on the server so we can get the data right away
useTracker = reactiveFn => reactiveFn();
}
else {
// @todo specify a default value for dependencies ? Omitting them can be very bad perf-wise.
useTracker = (reactiveFn, dependencies) => {
// Run the function once on mount without autorun or subscriptions,
// to get the initial return value.
// Note: maybe when React Suspense is officially available we could
// throw a Promise instead to skip the 1st render altogether ?
const [trackerData, setTrackerData] = useState(() => {
// We need to prevent subscriptions from running in that initial run.
const realSubscribe = Meteor.subscribe;
Meteor.subscribe = () => ({ stop: () => {}, ready: () => false });
const initialData = Tracker.nonreactive(reactiveFn);
Meteor.subscribe = realSubscribe;
return initialData;
});

useEffect(() => {
let computation;
// 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.
Tracker.nonreactive(() => {
computation = Tracker.autorun(() => {
const data = reactiveFn();
if (Package.mongo && Package.mongo.Mongo && data instanceof Package.mongo.Mongo.Cursor) {
console.warn(
'Warning: you are returning a Mongo cursor from useEffect. '
+ 'This value will not be reactive. You probably want to call '
+ '`.fetch()` on the cursor before returning it.'
);
}
setTrackerData(data);
});
});
return () => computation.stop();
}, dependencies);

return trackerData;
};
}

export default useTracker;
29 changes: 29 additions & 0 deletions packages/react-meteor-data/withTracker.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React, { memo } from 'react';
import useTracker from './useTracker.js';

export default function withTracker(options) {
return Component => {
const expandedOptions = typeof options === 'function' ? { getMeteorData: options } : options;
const { getMeteorData, pure = true } = expandedOptions;

function WithTracker(props) {
const data = useTracker(() => getMeteorData(props) || {}, [props]);

if (Package.mongo && Package.mongo.Mongo && data) {
Object.keys(data).forEach((key) => {
if (data[key] instanceof Package.mongo.Mongo.Cursor) {
console.warn(
'Warning: you are returning a Mongo cursor from withTracker. '
+ 'This value will not be reactive. You probably want to call '
+ '`.fetch()` on the cursor before returning it.'
);
}
});
}

return <Component {...{ ...props, ...data }} />;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just out of curiosity, is there a special reason against {...props} {...data}?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not really :-)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changed to {...props} {...data}

}

return pure ? memo(WithTracker) : WithTracker;
};
}