diff --git a/packages/react-meteor-hooks/.versions b/packages/react-meteor-hooks/.versions new file mode 100644 index 00000000..c09b67db --- /dev/null +++ b/packages/react-meteor-hooks/.versions @@ -0,0 +1,16 @@ +babel-compiler@7.0.0 +babel-runtime@1.2.0 +dynamic-import@0.3.0 +ecmascript@0.10.0 +ecmascript-runtime@0.5.0 +ecmascript-runtime-client@0.6.0 +ecmascript-runtime-server@0.5.0 +http@1.4.0 +meteor@1.8.2 +modules@0.11.3 +modules-runtime@0.9.1 +promise@0.10.1 +react-meteor-data@0.2.16 +tmeasday:check-npm-versions@0.3.2 +tracker@1.1.3 +url@1.2.0 diff --git a/packages/react-meteor-hooks/README.md b/packages/react-meteor-hooks/README.md new file mode 100644 index 00000000..3513f76a --- /dev/null +++ b/packages/react-meteor-hooks/README.md @@ -0,0 +1,59 @@ +## `react-meteor-data` + +This package provides an integration between React Hooks and [`Tracker`](https://atmospherejs.com/meteor/tracker), Meteor's reactive data system. + +### Install + +To install the package, use `meteor add`: + +```bash +meteor add react-meteor-hooks +``` + +You'll also need to install `react` if you have not already: + +```bash +npm install --save react@16.7.0.alpha.0 +``` + +### Hooks + +**`useMeteorSubscription`** + +It takes all arguments you would put into `Meteor.subscribe` and runs +the subscription and `ready()` checks in a `useEffect` and returns the +value of a state hook for `ready()`. + +**`useMeteorData`** + +This function takes a function as first parameter and runs that as an +effect hook within the meteor tracker. The second parameter is an +array of inputs that influence the effect hook. + +It returns the value of the state hook that represents the return +value of the given function. This can either be an object to be +destructured later or a single value (array of collection documents or +single collection document). + +### Usage + +This package exports the hooks `useMeteorSubscription` and `useMeteorData`. + +```js +export const Page = function (props) { + const loading = useMeteorSubscription('links'); + const links = useMeteorData(() => Links.find().fetch()); + + if(loading) return (
Loading links ...
); + + return ( + + ); +} +``` diff --git a/packages/react-meteor-hooks/package.js b/packages/react-meteor-hooks/package.js new file mode 100644 index 00000000..72f4010f --- /dev/null +++ b/packages/react-meteor-hooks/package.js @@ -0,0 +1,18 @@ +Package.describe({ + name: 'react-meteor-hooks', + summary: 'Proposal for react-hooks getting meteor data', + version: '0.1.0', + documentation: 'README.md', + git: 'https://github.com/meteor/react-packages', +}); + +Package.onUse(function (api) { + api.versionsFrom('1.3'); + api.use('tracker'); + api.use('ecmascript'); + api.use('tmeasday:check-npm-versions@0.3.2'); + + api.export([]); + + api.mainModule('react-meteor-hooks.jsx'); +}); diff --git a/packages/react-meteor-hooks/react-meteor-hooks.jsx b/packages/react-meteor-hooks/react-meteor-hooks.jsx new file mode 100644 index 00000000..00c7c155 --- /dev/null +++ b/packages/react-meteor-hooks/react-meteor-hooks.jsx @@ -0,0 +1,8 @@ +import { checkNpmVersions } from 'meteor/tmeasday:check-npm-versions'; + +checkNpmVersions({ + react: '16.7.0-alpha.0', +}, 'react-meteor-hooks'); + +export { default as useMeteorSubscription } from './useMeteorSubscription.jsx'; +export { default as useMeteorData } from './useMeteorData.jsx'; diff --git a/packages/react-meteor-hooks/useMeteorData.jsx b/packages/react-meteor-hooks/useMeteorData.jsx new file mode 100644 index 00000000..ff8f9b60 --- /dev/null +++ b/packages/react-meteor-hooks/useMeteorData.jsx @@ -0,0 +1,35 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { useEffect, useState } from 'react'; + +let useMeteorData; + +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 + useMeteorData = getMeteorData => getMeteorData(); +} else { + useMeteorData = (getMeteorData, inputs = []) => { + const [meteorData, setMeteorData] = useState(getMeteorData()); + let computation; + + const cleanUp = () => { + computation.stop(); + computation = null; + } + + useEffect(() => { + if(computation) cleanUp(); + + Tracker.autorun((currentComputation) => { + computation = currentComputation; + setMeteorData(getMeteorData()); + }); + + return cleanUp; + }, inputs); + + return meteorData; + } +} +export default useMeteorData; diff --git a/packages/react-meteor-hooks/useMeteorSubscription.jsx b/packages/react-meteor-hooks/useMeteorSubscription.jsx new file mode 100644 index 00000000..02113d43 --- /dev/null +++ b/packages/react-meteor-hooks/useMeteorSubscription.jsx @@ -0,0 +1,40 @@ +import { Meteor } from 'meteor/meteor'; +import { Tracker } from 'meteor/tracker'; +import { useEffect, useState } from 'react'; + +let useMeteorSubscription; + +if (Meteor.isServer) { + // When rendering on the server, we don't want to use the Tracker. + // The subscription is always ready on the server. + useMeteorSubscription = () => true; +} else { + useMeteorSubscription = (publication, ...parameters) => { + const [loading, setLoading] = useState(true); + let handle, computation; + + const cleanUp = () => { + handle && handle.stop(); + handle = null; + computation && computation.stop(); + computation = null; + } + + useEffect(() => { + if(computation) cleanUp(); + + Tracker.autorun((currentComputation) => { + computation = currentComputation; + + handle = Meteor.subscribe(publication, ...parameters); + setLoading(!handle.ready()); + }); + + return cleanUp; + }, [publication, ...parameters]); + + return loading; + } +} + +export default useMeteorSubscription;