diff --git a/packages/react-accounts/README.md b/packages/react-accounts/README.md new file mode 100644 index 00000000..fe22d5de --- /dev/null +++ b/packages/react-accounts/README.md @@ -0,0 +1,4 @@ +meteor/react-accounts +===================== + +A couple of simple hooks for accessing Meteor Accounts state. diff --git a/packages/react-accounts/package-lock.json b/packages/react-accounts/package-lock.json new file mode 100644 index 00000000..5bbc7597 --- /dev/null +++ b/packages/react-accounts/package-lock.json @@ -0,0 +1,430 @@ +{ + "name": "meteor-react-accounts", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz", + "integrity": "sha512-umhPIcMrlBZ2aTWlWjUseW9LjQKxi1dpFlQS8DzsxB//5K+u6GLTC/JliPKHsd5kJVPIU6X/Hy0YvWOYPcMxBw==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@jest/types": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.0.tgz", + "integrity": "sha512-8pDeq/JVyAYw7jBGU83v8RMYAkdrRxLG3BGnAJuqaQAUd6GWBmND2uyl+awI88+hit48suLoLjNFtR+ZXxWaYg==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@testing-library/dom": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.26.3.tgz", + "integrity": "sha512-/1P6taENE/H12TofJaS3L1J28HnXx8ZFhc338+XPR5y1E3g5ttOgu86DsGnV9/n2iPrfJQVUZ8eiGYZGSxculw==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.10.3", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.1", + "lz-string": "^1.4.4", + "pretty-format": "^26.4.2" + } + }, + "@testing-library/react": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz", + "integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==", + "requires": { + "@babel/runtime": "^7.10.3", + "@testing-library/dom": "^7.22.3" + } + }, + "@types/aria-query": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==" + }, + "@types/bson": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", + "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/meteor": { + "version": "1.4.60", + "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-1.4.60.tgz", + "integrity": "sha512-NsuIIKtGABovJHrE2H0+PUDlGTuvCL3UjX9fgxJOk43oRzmA+1FMOnGz4n1n9J6G6vbw9PumdWZOWTZkH/NnRw==", + "requires": { + "@types/connect": "*", + "@types/mongodb": "*", + "@types/react": "*", + "@types/underscore": "*" + } + }, + "@types/mongodb": { + "version": "3.5.30", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.30.tgz", + "integrity": "sha512-aKqOERMTA78LF5It0DeBRzFa4rXJ2Kmr+/EEG5GblSs0q1wRf3DqKErvQmFE8sFwsh5SBPuTU4h9yYHJY4dIJA==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.0.tgz", + "integrity": "sha512-BfbIHP9IapdupGhq/hc+jT5dyiBVZ2DdeC5WwJWQWDb0GijQlzUFAeIQn/2GtvZcd2HVUU7An8felIICFTC2qg==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz", + "integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/underscore": { + "version": "1.10.24", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz", + "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w==" + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + }, + "dom-accessibility-api": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "pretty-format": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.0.tgz", + "integrity": "sha512-Uumr9URVB7bm6SbaByXtx+zGlS+0loDkFMHP0kHahMjmfCtmFY03iqd++5v3Ld6iB5TocVXlBN/T+DXMn9d4BA==", + "requires": { + "@jest/types": "^26.6.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", + "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-test-renderer": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", + "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==" + } + } +} diff --git a/packages/react-accounts/package.js b/packages/react-accounts/package.js new file mode 100644 index 00000000..e14d1ec7 --- /dev/null +++ b/packages/react-accounts/package.js @@ -0,0 +1,17 @@ +/* global Package */ + +Package.describe({ + name: 'react-accounts', + summary: 'React hook for reactively tracking Meteor data', + version: '0.9.0', + documentation: 'README.md', + git: 'https://github.com/meteor/react-packages', +}); + +Package.onUse(function (api) { + api.versionsFrom('1.10'); + api.use('tracker'); + api.use('typescript'); + + api.mainModule('react-accounts.ts', ['client', 'server'], { lazy: true }); +}); diff --git a/packages/react-accounts/package.json b/packages/react-accounts/package.json new file mode 100644 index 00000000..2a3f6719 --- /dev/null +++ b/packages/react-accounts/package.json @@ -0,0 +1,15 @@ +{ + "name": "meteor-react-accounts", + "scripts": { + "make-types": "npx typescript *.ts --declaration --emitDeclarationOnly --outDir types" + }, + "dependencies": { + "@testing-library/react": "^10.0.2", + "@types/meteor": "^1.4.42", + "@types/react": "^16.9.34", + "react": "16.13.1", + "react-dom": "16.13.1", + "react-test-renderer": "16.13.1", + "typescript": "^4.0.3" + } +} diff --git a/packages/react-accounts/react-accounts.ts b/packages/react-accounts/react-accounts.ts new file mode 100644 index 00000000..38806cc7 --- /dev/null +++ b/packages/react-accounts/react-accounts.ts @@ -0,0 +1,29 @@ +import { Meteor } from 'meteor/meteor' +import { Tracker } from 'meteor/tracker' +import { useState, useEffect } from 'react' + +export const useUserId = () => { + const [userId, setUserId] = useState(Meteor.userId()) + useEffect(() => { + const computation = Tracker.autorun(() => { + setUserId(Meteor.userId()) + }) + return () => { + computation.stop() + } + }, []) + return userId +} + +export const useUser = () => { + const [user, setUser] = useState(Meteor.user()) + useEffect(() => { + const computation = Tracker.autorun(() => { + setUser(Meteor.user()) + }) + return () => { + computation.stop() + } + }, []) + return user +} diff --git a/packages/react-accounts/types/react-accounts.d.ts b/packages/react-accounts/types/react-accounts.d.ts new file mode 100644 index 00000000..e8332654 --- /dev/null +++ b/packages/react-accounts/types/react-accounts.d.ts @@ -0,0 +1,3 @@ +import { Meteor } from 'meteor/meteor'; +export declare const useUserId: () => string; +export declare const useUser: () => Meteor.User; diff --git a/packages/react-meteor-data/index.js b/packages/react-meteor-data/index.js index ba908523..5e7a4c36 100644 --- a/packages/react-meteor-data/index.js +++ b/packages/react-meteor-data/index.js @@ -4,9 +4,9 @@ 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.'); + console.warn('react-meteor-data 2+ requires React version >= 16.8.'); } } -export { default as useTracker } from './useTracker'; +export { default as useTracker, useTrackerLegacy } from './useTracker'; export { default as withTracker } from './withTracker.tsx'; diff --git a/packages/react-meteor-data/package-lock.json b/packages/react-meteor-data/package-lock.json index 86349087..99382a84 100644 --- a/packages/react-meteor-data/package-lock.json +++ b/packages/react-meteor-data/package-lock.json @@ -3,69 +3,107 @@ "requires": true, "lockfileVersion": 1, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, "@babel/runtime": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.2.tgz", - "integrity": "sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.5.tgz", + "integrity": "sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg==", "requires": { "regenerator-runtime": "^0.13.4" } }, "@babel/runtime-corejs3": { - "version": "7.9.2", - "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.9.2.tgz", - "integrity": "sha512-HHxmgxbIzOfFlZ+tdeRKtaxWOMUoCG5Mu3wKeUmOxjYrwb3AAHgnmtCUbPPK11/raIWLIBK250t8E2BPO0p7jA==", + "version": "7.12.5", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz", + "integrity": "sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ==", "requires": { "core-js-pure": "^3.0.0", "regenerator-runtime": "^0.13.4" } }, "@jest/types": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.3.0.tgz", - "integrity": "sha512-UkaDNewdqXAmCDbN2GlUM6amDKS78eCqiw/UmF5nE0mmLTd6moJkiZJML/X52Ke3LH7Swhw883IRXq8o9nWjVw==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", + "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", "requires": { "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "chalk": "^4.0.0" } }, "@testing-library/dom": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.2.1.tgz", - "integrity": "sha512-xIGoHlQ2ZiEL1dJIFKNmLDypzYF+4OJTTASRctl/aoIDaS5y/pRVHRigoqvPUV11mdJoR71IIgi/6UviMgyz4g==", + "version": "7.28.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.28.1.tgz", + "integrity": "sha512-acv3l6kDwZkQif/YqJjstT3ks5aaI33uxGNVIQmdKzbZ2eMKgg3EV2tB84GDdc72k3Kjhl6mO8yUt6StVIdRDg==", "requires": { - "@babel/runtime": "^7.9.2", - "@types/testing-library__dom": "^7.0.0", - "aria-query": "^4.0.2", - "dom-accessibility-api": "^0.4.2", - "pretty-format": "^25.1.0" + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.4", + "lz-string": "^1.4.4", + "pretty-format": "^26.6.2" } }, "@testing-library/react": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.0.2.tgz", - "integrity": "sha512-YT6Mw0oJz7R6vlEkmo1FlUD+K15FeXApOB5Ffm9zooFVnrwkt00w18dUJFMOh1yRp9wTdVRonbor7o4PIpFCmA==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.2.tgz", + "integrity": "sha512-jaxm0hwUjv+hzC+UFEywic7buDC9JQ1q3cDsrWVSDAPmLotfA6E6kUHlYm/zOeGCac6g48DR36tFHxl7Zb+N5A==", "requires": { - "@babel/runtime": "^7.9.2", - "@testing-library/dom": "^7.1.0", - "@types/testing-library__react": "^10.0.0" + "@babel/runtime": "^7.12.5", + "@testing-library/dom": "^7.28.1" } }, + "@types/aria-query": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==" + }, "@types/bson": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", - "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", + "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", "requires": { "@types/node": "*" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" - }, "@types/connect": { "version": "3.4.33", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", @@ -75,9 +113,9 @@ } }, "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" }, "@types/istanbul-lib-report": { "version": "3.0.0", @@ -88,18 +126,17 @@ } }, "@types/istanbul-reports": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", - "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", "requires": { - "@types/istanbul-lib-coverage": "*", "@types/istanbul-lib-report": "*" } }, "@types/meteor": { - "version": "1.4.42", - "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-1.4.42.tgz", - "integrity": "sha512-gaYksL5mAhvEAgGYZyz1HOYTSZx4Fg3RG9yA3qSGi0bFbvut3lbTVORU7Ry2Z29n//ghgt+grxVmCIXBPjfcLw==", + "version": "1.4.64", + "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-1.4.64.tgz", + "integrity": "sha512-Yxsn70JGZlNDaYD2OX/Ho6pKVmWTVT70JEVO7PbGMM9O4sfOjWSVUvJ2JJ5Dt1X6+sLojkAM5WQm7Jp75S9Vfg==", "requires": { "@types/connect": "*", "@types/mongodb": "*", @@ -108,18 +145,18 @@ } }, "@types/mongodb": { - "version": "3.5.5", - "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.5.tgz", - "integrity": "sha512-iIWBu740IPolqzpYUBdQs1T4LMiJv1CIIcJb6gX2mGtb/VmnFWONSiyUG+lJF4IUg+QEuIqpalW3eshiTCc8dQ==", + "version": "3.5.34", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.34.tgz", + "integrity": "sha512-73iy3+MiH+wxSM+hVA5jcW9ZTUaor2WKvM7hW+htOSgVb7E6/JBHOWaxj7rL1/vaxEBziKRr/VPecy3YAKqLuQ==", "requires": { "@types/bson": "*", "@types/node": "*" } }, "@types/node": { - "version": "13.11.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.11.1.tgz", - "integrity": "sha512-eWQGP3qtxwL8FGneRrC5DwrJLGN4/dH1clNTuLfN81HCrxVtxRjygDTUoZJ5ASlDEeo0ppYFQjQIlXhtXpOn6g==" + "version": "14.14.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.10.tgz", + "integrity": "sha512-J32dgx2hw8vXrSbu4ZlVhn1Nm3GbeCFNw2FWL8S5QKucHGY0cyNwjdQdO+KMBZ4wpmC7KhLCiNsdk1RFRIYUQQ==" }, "@types/prop-types": { "version": "15.7.3", @@ -127,49 +164,23 @@ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" }, "@types/react": { - "version": "16.9.34", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz", - "integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-17.0.0.tgz", + "integrity": "sha512-aj/L7RIMsRlWML3YB6KZiXB3fV2t41+5RBGYF8z+tAKU43Px8C3cYUZsDvf1/+Bm4FK21QWBrDutu8ZJ/70qOw==", "requires": { "@types/prop-types": "*", - "csstype": "^2.2.0" - } - }, - "@types/react-dom": { - "version": "16.9.6", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.6.tgz", - "integrity": "sha512-S6ihtlPMDotrlCJE9ST1fRmYrQNNwfgL61UB4I1W7M6kPulUKx9fXAleW5zpdIjUQ4fTaaog8uERezjsGUj9HQ==", - "requires": { - "@types/react": "*" - } - }, - "@types/testing-library__dom": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@types/testing-library__dom/-/testing-library__dom-7.0.1.tgz", - "integrity": "sha512-WokGRksRJb3Dla6h02/0/NNHTkjsj4S8aJZiwMj/5/UL8VZ1iCe3H8SHzfpmBeH8Vp4SPRT8iC2o9kYULFhDIw==", - "requires": { - "pretty-format": "^25.1.0" - } - }, - "@types/testing-library__react": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/testing-library__react/-/testing-library__react-10.0.1.tgz", - "integrity": "sha512-RbDwmActAckbujLZeVO/daSfdL1pnjVqas25UueOkAY5r7vriavWf0Zqg7ghXMHa8ycD/kLkv8QOj31LmSYwww==", - "requires": { - "@types/react-dom": "*", - "@types/testing-library__dom": "*", - "pretty-format": "^25.1.0" + "csstype": "^3.0.2" } }, "@types/underscore": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.9.4.tgz", - "integrity": "sha512-CjHWEMECc2/UxOZh0kpiz3lEyX2Px3rQS9HzD20lxMvx571ivOBQKeLnqEjxUY0BMgp6WJWo/pQLRBwMW5v4WQ==" + "version": "1.10.24", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz", + "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w==" }, "@types/yargs": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", - "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "version": "15.0.10", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.10.tgz", + "integrity": "sha512-z8PNtlhrj7eJNLmrAivM7rjBESG6JwC5xP3RVk12i/8HVP7Xnx/sEmERnRImyEuUaJfO942X0qMOYsoupaJbZQ==", "requires": { "@types/yargs-parser": "*" } @@ -185,64 +196,104 @@ "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" }, "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" + "color-convert": "^1.9.0" } }, "aria-query": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.0.2.tgz", - "integrity": "sha512-S1G1V790fTaigUSM/Gd0NngzEfiMy9uTUfMyHhKhVyy4cH5O/eTuR01ydhGL0z4Za1PXFTRGH3qL8VhUQuEO5w==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", "requires": { - "@babel/runtime": "^7.7.4", - "@babel/runtime-corejs3": "^7.7.4" + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" } }, "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } } }, "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "requires": { - "color-name": "~1.1.4" + "color-name": "1.1.3" } }, "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" }, "core-js-pure": { - "version": "3.6.5", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", - "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.8.0.tgz", + "integrity": "sha512-fRjhg3NeouotRoIV0L1FdchA6CK7ZD+lyINyMoz19SyV+ROpC4noS1xItWHFtwZdlqfMfVPJEyEGdfri2bD1pA==" }, "csstype": { - "version": "2.6.10", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz", - "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==" + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" }, "dom-accessibility-api": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.4.3.tgz", - "integrity": "sha512-JZ8iPuEHDQzq6q0k7PKMGbrIdsgBB7TRrtVOUm4nSMCExlg5qQG4KXWTH2k90yggjM4tTumRGwTKJSldMzKyLA==" + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" }, "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" }, "js-tokens": { "version": "4.0.0", @@ -257,90 +308,120 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "pretty-format": { - "version": "25.3.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.3.0.tgz", - "integrity": "sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA==", + "version": "26.6.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", + "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", "requires": { - "@jest/types": "^25.3.0", + "@jest/types": "^26.6.2", "ansi-regex": "^5.0.0", "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "react-is": "^17.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } } }, "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react/-/react-17.0.1.tgz", + "integrity": "sha512-lG9c9UuMHdcAexXtigOZLX8exLWkW0Ku29qPRU8uhF2R9BN96dLCt0psvzPLlHc5OWkgymP3qwTRgbnw5BKx3w==", "requires": { "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "object-assign": "^4.1.1" } }, "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.1.tgz", + "integrity": "sha512-6eV150oJZ9U2t9svnsspTMrWNyHc6chX0KzDeAOXftRa8bNeOKTTfCJ7KorIwenkHd2xqVTBTCZd79yk/lx/Ug==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "scheduler": "^0.20.1" } }, "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz", + "integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==" + }, + "react-shallow-renderer": { + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz", + "integrity": "sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg==", + "requires": { + "object-assign": "^4.1.1", + "react-is": "^16.12.0 || ^17.0.0" + } }, "react-test-renderer": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", - "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", + "version": "17.0.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-17.0.1.tgz", + "integrity": "sha512-/dRae3mj6aObwkjCcxZPlxDFh73XZLgvwhhyON2haZGUEhiaY5EjfAdw+d/rQmlcFwdTpMXCSGVk374QbCTlrA==", "requires": { "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "react-is": "^16.8.6", - "scheduler": "^0.19.1" + "react-is": "^17.0.1", + "react-shallow-renderer": "^16.13.1", + "scheduler": "^0.20.1" } }, "regenerator-runtime": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz", - "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==" + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" }, "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.1.tgz", + "integrity": "sha512-LKTe+2xNJBNxu/QhHvDR14wUXHRQbVY5ZOYpOGWRzhydZUqrLb2JBvLPY7cAqFmqrWuDED0Mjk7013SZiOz6Bw==", "requires": { "loose-envify": "^1.1.0", "object-assign": "^4.1.1" } }, "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "requires": { - "has-flag": "^4.0.0" + "has-flag": "^3.0.0" } + }, + "typescript": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", + "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==" } } } diff --git a/packages/react-meteor-data/package.json b/packages/react-meteor-data/package.json index f2ad7573..eba333e5 100644 --- a/packages/react-meteor-data/package.json +++ b/packages/react-meteor-data/package.json @@ -1,11 +1,15 @@ { "name": "react-meteor-data", + "scripts": { + "make-types": "npx typescript *.ts --declaration --emitDeclarationOnly --outDir types" + }, "dependencies": { - "react": "16.13.1", - "react-dom": "16.13.1", - "react-test-renderer": "16.13.1", - "@testing-library/react": "^10.0.2", - "@types/meteor": "^1.4.42", - "@types/react": "^16.9.34" + "@testing-library/react": "^11.2.2", + "@types/meteor": "^1.4.64", + "@types/react": "^17.0.0", + "react": "17.0.1", + "react-dom": "17.0.1", + "react-test-renderer": "17.0.1", + "typescript": "^4.1.2" } } diff --git a/packages/react-meteor-data/types/useTracker.d.ts b/packages/react-meteor-data/types/useTracker.d.ts new file mode 100644 index 00000000..8400196c --- /dev/null +++ b/packages/react-meteor-data/types/useTracker.d.ts @@ -0,0 +1,7 @@ +/// +import { Tracker } from 'meteor/tracker'; +import { DependencyList } from 'react'; +declare type ReactiveFn = (c?: Tracker.Computation) => any; +declare const _default: (reactiveFn: () => T, deps: DependencyList) => T; +export default _default; +export declare const useTrackerLegacy: ((reactiveFn: ReactiveFn) => any) | ((reactiveFn: () => T, deps: DependencyList) => T); diff --git a/packages/react-meteor-data/useTracker.tests.js b/packages/react-meteor-data/useTracker.tests.js index ea04cd80..02dc5095 100644 --- a/packages/react-meteor-data/useTracker.tests.js +++ b/packages/react-meteor-data/useTracker.tests.js @@ -5,7 +5,7 @@ import { cleanup, waitFor } from '@testing-library/react'; import { ReactiveDict } from 'meteor/reactive-dict'; import { ReactiveVar } from 'meteor/reactive-var'; -import useTracker from './useTracker'; +import useTracker, { useTrackerLegacy } from './useTracker'; if (Meteor.isClient) { const getInnerHtml = function (elem) { @@ -22,22 +22,12 @@ if (Meteor.isClient) { const reactiveDict = new ReactiveDict(); let runCount = 0; - let computation; - let createdCount = 0; - let destroyedCount = 0; let value; const Test = () => { - value = useTracker(() => { + value = useTrackerLegacy(() => { runCount++; reactiveDict.setDefault('key', 'initial'); return reactiveDict.get('key'); - }, null, (c) => { - test.isFalse(c === computation, 'The new computation should always be a new instance'); - computation = c; - createdCount++; - return () => { - destroyedCount++; - } }); return {value}; }; @@ -63,10 +53,6 @@ if (Meteor.isClient) { test.equal(value, 'initial', 'Expect initial value to be "initial"'); test.equal(runCount, 2 * strictMul, 'Should have run 2 times - first, and in useEffect'); - if (Meteor.isClient) { - test.equal(createdCount, 2 * strictMul, 'Should have been created 2 times'); - test.equal(destroyedCount, 2 * strictMul - 1, 'Should have been destroyed 1 time'); - } await waitFor(() => { reactiveDict.set('key', 'changed'); @@ -75,10 +61,6 @@ if (Meteor.isClient) { test.equal(value, 'changed', 'Expect new value to be "changed"'); test.equal(runCount, 3 * strictMul, 'Should have run 3 times'); - if (Meteor.isClient) { - test.equal(createdCount, 3 * strictMul, 'Should have been created 3 times'); - test.equal(destroyedCount, 3 * strictMul - 1, 'Should have been destroyed 1 less than created'); - } await waitFor(() => { rerender(); @@ -86,20 +68,12 @@ if (Meteor.isClient) { test.equal(value, 'changed', 'Expect value of "changed" to persist after rerender'); test.equal(runCount, 4 * strictMul, 'Should have run 4 times'); - if (Meteor.isClient) { - test.equal(createdCount, 4 * strictMul, 'Should have been created 4 times'); - test.equal(destroyedCount, 4 * strictMul -1, 'Should have been destroyed 1 less than created'); - } await waitFor(() => { unmount(); }, { container, timeout: 250 }); test.equal(runCount, 4 * strictMul, 'Unmount should not cause a tracker run'); - if (Meteor.isClient) { - test.equal(createdCount, 4 * strictMul, 'Should have been created 4 times'); - test.equal(destroyedCount, 4 * strictMul, 'Should have been destroyed the same number of times as created'); - } await waitFor(() => { reactiveDict.set('different', 'changed again'); @@ -107,21 +81,17 @@ if (Meteor.isClient) { }, { container, timeout: 250 }); test.equal(value, 'changed', 'After unmount, changes to the reactive source should not update the value.'); - test.equal(runCount, 4 * strictMul, 'After unmount, useTracker should no longer be tracking'); - if (Meteor.isClient) { - test.equal(createdCount, 4 * strictMul, 'Should have been created 4 times'); - test.equal(destroyedCount, 4 * strictMul, 'Should have been destroyed the same number of times as created'); - } + test.equal(runCount, 4 * strictMul, 'After unmount, useTrackerLegacy should no longer be tracking'); reactiveDict.destroy(); }; - Tinytest.addAsync('useTracker - no deps', async function (test, completed) { + Tinytest.addAsync('useTrackerLegacy - Normal', async function (test, completed) { await noDepsTester(test); completed(); }); - Tinytest.addAsync('useTracker - no deps in StrictMode', async function (test, completed) { + Tinytest.addAsync('useTrackerLegacy - in StrictMode', async function (test, completed) { await noDepsTester(test, 'strict-mode'); completed(); }); @@ -131,23 +101,13 @@ if (Meteor.isClient) { const reactiveDict = new ReactiveDict(); let runCount = 0; - let computation; - let createdCount = 0; - let destroyedCount = 0; let value; const Test = ({ name }) => { value = useTracker(() => { runCount++; reactiveDict.setDefault(name, 'initial'); return reactiveDict.get(name); - }, [name], (c) => { - test.isFalse(c === computation, 'The new computation should always be a new instance'); - computation = c; - createdCount++; - return () => { - destroyedCount++; - } - }); + }, [name]); return {value}; }; @@ -182,11 +142,7 @@ if (Meteor.isClient) { await waitFor(() => {}, { container, timeout: 250 }); test.equal(value, 'initial', 'Expect the initial value for given name to be "initial"'); - test.equal(runCount, 1 + strictAdd, 'Should have run 1 times - still only the sync invocation (unlike without deps)'); - if (Meteor.isClient) { - test.equal(createdCount, 1 + strictAdd, 'Should have been created 1 times'); - test.equal(destroyedCount, 0, 'Should not have been destroyed yet'); - } + test.equal(runCount, 2 + strictAdd, 'Should have run 2 times'); await waitFor(() => { reactiveDict.set('name', 'changed'); @@ -194,22 +150,14 @@ if (Meteor.isClient) { }, { container, timeout: 250 }); test.equal(value, 'changed', 'Expect the new value for given name to be "changed"'); - test.equal(runCount, 2 + strictAdd, 'Should have run 2 times after reactive change'); - if (Meteor.isClient) { - test.equal(createdCount, 1 + strictAdd, 'Should have been created 1 times'); - test.equal(destroyedCount, 1 + strictAdd - 1, 'Should not have been destroyed yet'); - } + test.equal(runCount, 3 + strictAdd, 'Should have run 3 times after reactive change'); await waitFor(() => { rerender(); }, { container, timeout: 250 }); test.equal(value, 'changed', 'Expect the new value "changed" for given name to have persisted through render'); - test.equal(runCount, 2 + strictAdd, 'Should still have run only 2 times'); - if (Meteor.isClient) { - test.equal(createdCount, 1 + strictAdd, 'Should have been created 1 times'); - test.equal(destroyedCount, 1 + strictAdd - 1, 'Should not have been destroyed yet'); - } + test.equal(runCount, 3 + strictAdd, 'Should still have run only 3 times'); await waitFor(() => { rerender('different'); @@ -217,35 +165,61 @@ if (Meteor.isClient) { if (mode === 'strict-mode') strictAdd++; test.equal(value, 'initial', 'After deps change, the initial value should have returned'); - test.equal(runCount, 3 + strictAdd, 'Should have run 3 times'); - if (Meteor.isClient) { - test.equal(createdCount, 2 + strictAdd, 'Should have been created 2 times'); - test.equal(destroyedCount, 2 + strictAdd - 1, 'Should have been destroyed 1 times'); - } + test.equal(runCount, 5 + strictAdd, 'Should have run 5 times'); await waitFor(() => { unmount(); }, { container, timeout: 250 }); - test.equal(runCount, 3 + strictAdd, 'Unmount should not cause a tracker run'); - if (Meteor.isClient) { - test.equal(createdCount, 2 + strictAdd, 'Should have been created 2 times'); - test.equal(destroyedCount, 2 + strictAdd, 'Should have been destroyed 2 times'); - } - + test.equal(runCount, 5 + strictAdd, 'Unmount should not cause a tracker run'); reactiveDict.destroy(); }; - Tinytest.addAsync('useTracker - with deps', async function (test, completed) { + Tinytest.addAsync('useTracker - Normal', async function (test, completed) { await depsTester(test); completed(); }); - Tinytest.addAsync('useTracker - with deps in StrictMode', async function (test, completed) { + Tinytest.addAsync('useTracker - in StrictMode', async function (test, completed) { await depsTester(test, 'strict-mode'); completed(); }); + Tinytest.addAsync('useTrackerLegacy - basic track', async function (test, completed) { + var container = document.createElement("DIV"); + + var x = new ReactiveVar('aaa'); + + var Foo = () => { + const data = useTrackerLegacy(() => { + return { + x: x.get() + }; + }); + return {data.x}; + }; + + ReactDOM.render(, container); + test.equal(getInnerHtml(container), 'aaa'); + + x.set('bbb'); + await waitFor(() => { + Tracker.flush({_throwFirstError: true}); + }, { container, timeout: 250 }); + + test.equal(getInnerHtml(container), 'bbb'); + + test.equal(x._numListeners(), 1); + + await waitFor(() => { + ReactDOM.unmountComponentAtNode(container); + }, { container, timeout: 250 }); + + test.equal(x._numListeners(), 0); + + completed(); + }); + Tinytest.addAsync('useTracker - basic track', async function (test, completed) { var container = document.createElement("DIV"); @@ -256,7 +230,7 @@ if (Meteor.isClient) { return { x: x.get() }; - }); + }, []); return {data.x}; }; @@ -281,6 +255,44 @@ if (Meteor.isClient) { completed(); }); + // Make sure that calling ReactDOM.render() from an autorun doesn't + // associate that autorun with the mixin's autorun. When autoruns are + // nested, invalidating the outer one stops the inner one, unless + // Tracker.nonreactive is used. This test tests for the use of + // Tracker.nonreactive around the mixin's autorun. + Tinytest.addAsync('useTrackerLegacy - render in autorun', async function (test, completed) { + var container = document.createElement("DIV"); + + var x = new ReactiveVar('aaa'); + + var Foo = () => { + const data = useTrackerLegacy(() => { + return { + x: x.get() + }; + }); + return {data.x}; + }; + + Tracker.autorun(function (c) { + ReactDOM.render(, container); + // Stopping this autorun should not affect the mixin's autorun. + c.stop(); + }); + test.equal(getInnerHtml(container), 'aaa'); + + x.set('bbb'); + await waitFor(() => { + Tracker.flush({_throwFirstError: true}); + }, { container, timeout: 250 }); + test.equal(getInnerHtml(container), 'bbb'); + + ReactDOM.unmountComponentAtNode(container); + + completed(); + }); + + // Make sure that calling ReactDOM.render() from an autorun doesn't // associate that autorun with the mixin's autorun. When autoruns are // nested, invalidating the outer one stops the inner one, unless @@ -296,7 +308,7 @@ if (Meteor.isClient) { return { x: x.get() }; - }); + }, []); return {data.x}; }; @@ -318,7 +330,7 @@ if (Meteor.isClient) { completed(); }); - Tinytest.addAsync('useTracker - track based on props and state', async function (test, completed) { + Tinytest.addAsync('useTrackerLegacy - track based on props and state', async function (test, completed) { var container = document.createElement("DIV"); var xs = [new ReactiveVar('aaa'), @@ -329,7 +341,7 @@ if (Meteor.isClient) { var Foo = (props) => { const [state, _setState] = useState({ m: 0 }); setState = _setState; - const data = useTracker(() => { + const data = useTrackerLegacy(() => { return { x: xs[state.m + props.n].get() }; @@ -387,7 +399,7 @@ if (Meteor.isClient) { completed(); }); - Tinytest.addAsync('useTracker - track based on props and state (with deps)', async function (test, completed) { + Tinytest.addAsync('useTracker - track based on props and state', async function (test, completed) { var container = document.createElement("DIV"); var xs = [new ReactiveVar('aaa'), @@ -457,6 +469,111 @@ if (Meteor.isClient) { }); }; + testAsyncMulti('useTrackerLegacy - resubscribe', [ + function (test, expect) { + var self = this; + self.div = document.createElement("DIV"); + self.collection = new Mongo.Collection("useTrackerLegacy-mixin-coll"); + self.num = new ReactiveVar(1); + self.someOtherVar = new ReactiveVar('foo'); + self.Foo = () => { + const data = useTrackerLegacy(() => { + self.handle = + Meteor.subscribe("useTrackerLegacy-mixin-sub", + self.num.get()); + + return { + v: self.someOtherVar.get(), + docs: self.collection.find().fetch() + }; + }); + self.data = data; + return
{ + _.map(data.docs, (doc) => {doc._id}) + }
; + }; + + self.component = ReactDOM.render(, self.div); + test.equal(getInnerHtml(self.div), '
', 'div should be empty'); + + var handle = self.handle; + test.isFalse(handle.ready(), 'handle.ready() should be false'); + + waitForTracker(() => handle.ready(), + expect()); + }, + function (test, expect) { + var self = this; + test.isTrue(self.handle.ready(), 'self.handle.ready() should be true'); + test.equal(getInnerHtml(self.div), '
id1
', 'div should contain id1'); + + self.someOtherVar.set('bar'); + self.oldHandle1 = self.handle; + + // can't call Tracker.flush() here (we are in a Tracker.flush already) + Tracker.afterFlush(expect()); + }, + function (test, expect) { + var self = this; + var oldHandle = self.oldHandle1; + var newHandle = self.handle; + test.notEqual(oldHandle, newHandle, 'handles should be different instances'); // new handle + test.equal(newHandle.subscriptionId, oldHandle.subscriptionId, 'subscriptionId should be different'); // same sub + test.isTrue(newHandle.ready(), 'newHandle.ready() should be true'); // doesn't become unready + // no change to the content + test.equal(getInnerHtml(self.div), '
id1
', 'div should contain id1'); + + // ok, now change the `num` argument to the subscription + self.num.set(2); + self.oldHandle2 = newHandle; + Tracker.afterFlush(expect()); + }, + function (test, expect) { + var self = this; + // data is still there + test.equal(getInnerHtml(self.div), '
id1
', 'div shold contain id1'); + // handle is no longer ready + var handle = self.handle; + test.isFalse(handle.ready(), 'handle.ready() should be false'); + // different sub ID + test.isTrue(self.oldHandle2.subscriptionId, 'self.oldHandle2.subscriptionId should be truthy'); + test.isTrue(handle.subscriptionId, 'handle.subscriptionId should be truthy'); + test.notEqual(handle.subscriptionId, self.oldHandle2.subscriptionId, 'subscriptionId should match'); + + waitForTracker(() => handle.ready(), + expect()); + }, + function (test, expect) { + var self = this; + // now we see the new data! (and maybe the old data, because + // when a subscription goes away, its data doesn't disappear right + // away; the server has to tell the client which documents or which + // properties to remove, and this is not easy to wait for either; see + // https://github.com/meteor/meteor/issues/2440) + test.equal(getInnerHtml(self.div).replace('id1', ''), + '
id2
'); + + self.someOtherVar.set('baz'); + self.oldHandle3 = self.handle; + + Tracker.afterFlush(expect()); + }, + function (test, expect) { + var self = this; + test.equal(self.data.v, 'baz', 'self.data.v should be "baz"'); + test.notEqual(self.oldHandle3, self.handle, 'oldHandle3 shold match self.handle'); + test.equal(self.oldHandle3.subscriptionId, + self.handle.subscriptionId, 'same for subscriptionId'); + test.isTrue(self.handle.ready(), 'self.handle.ready() should be true'); + }, + function (test, expect) { + ReactDOM.unmountComponentAtNode(this.div); + // break out of flush time, so we don't call the test's + // onComplete from within Tracker.flush + Meteor.defer(expect()); + } + ]); + testAsyncMulti('useTracker - resubscribe', [ function (test, expect) { var self = this; @@ -474,7 +591,7 @@ if (Meteor.isClient) { v: self.someOtherVar.get(), docs: self.collection.find().fetch() }; - }); + }, []); self.data = data; return
{ _.map(data.docs, (doc) => {doc._id}) @@ -482,18 +599,18 @@ if (Meteor.isClient) { }; self.component = ReactDOM.render(, self.div); - test.equal(getInnerHtml(self.div), '
'); + test.equal(getInnerHtml(self.div), '
', 'div should be empty'); var handle = self.handle; - test.isFalse(handle.ready()); + test.isFalse(handle.ready(), 'handle.ready() should be false'); waitForTracker(() => handle.ready(), expect()); }, function (test, expect) { var self = this; - test.isTrue(self.handle.ready()); - test.equal(getInnerHtml(self.div), '
id1
'); + test.isTrue(self.handle.ready(), 'self.handle.ready() should be true'); + test.equal(getInnerHtml(self.div), '
id1
', 'div should contain id1'); self.someOtherVar.set('bar'); self.oldHandle1 = self.handle; @@ -505,11 +622,11 @@ if (Meteor.isClient) { var self = this; var oldHandle = self.oldHandle1; var newHandle = self.handle; - test.notEqual(oldHandle, newHandle); // new handle - test.equal(newHandle.subscriptionId, oldHandle.subscriptionId); // same sub - test.isTrue(newHandle.ready()); // doesn't become unready + test.notEqual(oldHandle, newHandle, 'handles should be different instances'); // new handle + test.equal(newHandle.subscriptionId, oldHandle.subscriptionId, 'subscriptionId should be different'); // same sub + test.isTrue(newHandle.ready(), 'newHandle.ready() should be true'); // doesn't become unready // no change to the content - test.equal(getInnerHtml(self.div), '
id1
'); + test.equal(getInnerHtml(self.div), '
id1
', 'div should contain id1'); // ok, now change the `num` argument to the subscription self.num.set(2); @@ -519,14 +636,14 @@ if (Meteor.isClient) { function (test, expect) { var self = this; // data is still there - test.equal(getInnerHtml(self.div), '
id1
'); + test.equal(getInnerHtml(self.div), '
id1
', 'div shold contain id1'); // handle is no longer ready var handle = self.handle; - test.isFalse(handle.ready()); + test.isFalse(handle.ready(), 'handle.ready() should be false'); // different sub ID - test.isTrue(self.oldHandle2.subscriptionId); - test.isTrue(handle.subscriptionId); - test.notEqual(handle.subscriptionId, self.oldHandle2.subscriptionId); + test.isTrue(self.oldHandle2.subscriptionId, 'self.oldHandle2.subscriptionId should be truthy'); + test.isTrue(handle.subscriptionId, 'handle.subscriptionId should be truthy'); + test.notEqual(handle.subscriptionId, self.oldHandle2.subscriptionId, 'subscriptionId should match'); waitForTracker(() => handle.ready(), expect()); @@ -548,11 +665,11 @@ if (Meteor.isClient) { }, function (test, expect) { var self = this; - test.equal(self.data.v, 'baz'); - test.notEqual(self.oldHandle3, self.handle); + test.equal(self.data.v, 'baz', 'self.data.v should be "baz"'); + test.notEqual(self.oldHandle3, self.handle, 'oldHandle3 shold match self.handle'); test.equal(self.oldHandle3.subscriptionId, - self.handle.subscriptionId); - test.isTrue(self.handle.ready()); + self.handle.subscriptionId, 'same for subscriptionId'); + test.isTrue(self.handle.ready(), 'self.handle.ready() should be true'); }, function (test, expect) { ReactDOM.unmountComponentAtNode(this.div); @@ -595,6 +712,12 @@ if (Meteor.isClient) { // }); } else { + Meteor.publish("useTrackerLegacy-mixin-sub", function (num) { + Meteor.defer(() => { // because subs are blocking + this.added("useTrackerLegacy-mixin-coll", 'id'+num, {}); + this.ready(); + }); + }); Meteor.publish("useTracker-mixin-sub", function (num) { Meteor.defer(() => { // because subs are blocking this.added("useTracker-mixin-coll", 'id'+num, {}); diff --git a/packages/react-meteor-data/useTracker.ts b/packages/react-meteor-data/useTracker.ts index 76261a7e..06275439 100644 --- a/packages/react-meteor-data/useTracker.ts +++ b/packages/react-meteor-data/useTracker.ts @@ -1,10 +1,10 @@ declare var Package: any import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; -import { useReducer, useEffect, useRef, useMemo } from 'react'; +import { useReducer, useState, useEffect, useRef, useMemo, DependencyList } from 'react'; // Warns if data is a Mongo.Cursor or a POJO containing a Mongo.Cursor. -function checkCursor(data: any): void { +function checkCursor (data: any): void { let shouldWarn = false; if (Package.mongo && Package.mongo.Mongo && data && typeof data === 'object') { if (data instanceof Package.mongo.Mongo.Cursor) { @@ -29,56 +29,29 @@ function checkCursor(data: any): void { // Used to create a forceUpdate from useReducer. Forces update by // incrementing a number whenever the dispatch method is invoked. const fur = (x: number): number => x + 1; -const useForceUpdate = (): CallableFunction => { - const [, forceUpdate] = useReducer(fur, 0); - return forceUpdate; -} +const useForceUpdate = () => useReducer(fur, 0)[1]; type ReactiveFn = (c?: Tracker.Computation) => any; -type ComputationHandler = (c: Tracker.Computation) => () => void | void; type TrackerRefs = { - reactiveFn: ReactiveFn; - computationHandler?: ComputationHandler; - deps?: Array; computation?: Tracker.Computation; isMounted: boolean; - disposeId?: ReturnType; trackerData: any; - computationCleanup?: () => void; - trackerCount?: number } -// The follow functions were hoisted out of the closure to reduce allocations. -// Since they no longer have access to the local vars, we pass them in and mutate here. -const dispose = (refs: TrackerRefs): void => { - if (refs.computationCleanup) { - refs.computationCleanup(); - delete refs.computationCleanup; - } +const useTrackerNoDeps = (reactiveFn: ReactiveFn) => { + const { current: refs } = useRef({ + isMounted: false, + trackerData: null + }); + const forceUpdate = useForceUpdate(); + + // Without deps, always dispose and recreate the computation with every render. if (refs.computation) { refs.computation.stop(); - refs.computation = null; + // @ts-ignore This makes TS think ref.computation is "never" set + refs.computation; } -}; -const runReactiveFn = Meteor.isDevelopment - ? (refs: TrackerRefs, c: Tracker.Computation): void => { - const data = refs.reactiveFn(c); - checkCursor(data); - refs.trackerData = data; - } - : (refs: TrackerRefs, c: Tracker.Computation): void => { - refs.trackerData = refs.reactiveFn(c); - }; - -const clear = (refs: TrackerRefs): void => { - if (refs.disposeId) { - clearTimeout(refs.disposeId); - delete refs.disposeId; - } -}; - -const track = (refs: TrackerRefs, forceUpdate: Function, trackedFn: Function): void => { // 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 @@ -86,79 +59,18 @@ const track = (refs: TrackerRefs, forceUpdate: Function, trackedFn: Function): v // it stops the inner one. Tracker.nonreactive(() => Tracker.autorun((c: Tracker.Computation) => { refs.computation = c; - trackedFn(c, refs, forceUpdate); - })); -}; - -const doFirstRun = (refs: TrackerRefs, c: Tracker.Computation): void => { - // If there is a computationHandler, pass it the computation, and store the - // result, which may be a cleanup method. - if (refs.computationHandler) { - const cleanupHandler = refs.computationHandler(c); - if (cleanupHandler) { - if (Meteor.isDevelopment && typeof cleanupHandler !== 'function') { - console.warn( - 'Warning: Computation handler should return a function ' - + 'to be used for cleanup or return nothing.' - ); - } - refs.computationCleanup = cleanupHandler; - } - } - // Always run the reactiveFn on firstRun - runReactiveFn(refs, c); -} - -const tracked = (c: Tracker.Computation, refs: TrackerRefs, forceUpdate: Function): void => { - if (c.firstRun) { - doFirstRun(refs, c); - } else { - if (refs.isMounted) { - // Only run the reactiveFn if the component is mounted. - runReactiveFn(refs, c); - forceUpdate(); - } else { - // If we got here, then a reactive update happened before the render was - // committed - before useEffect has run. We don't want to run the reactiveFn - // while we are not sure this render will be committed, so we'll dispose of the - // computation, and set everything up to be restarted in useEffect if needed. - // NOTE: If we don't run the user's reactiveFn when a computation updates, we'll - // leave the computation in a non-reactive state - so we need to dispose here - // and let useEffect recreate the computation later. - dispose(refs); - // Might as well clear the timeout! - clear(refs); - } - } -}; - -interface useTrackerSignature { - (reactiveFn: ReactiveFn, deps?: null | Array, computationHandler?: ComputationHandler): any -} - -const useTrackerNoDeps: useTrackerSignature = (reactiveFn, deps = null, computationHandler) => { - const { current: refs } = useRef({ - reactiveFn, - isMounted: false, - trackerData: null - }); - const forceUpdate = useForceUpdate(); - - refs.reactiveFn = reactiveFn; - if (computationHandler) { - refs.computationHandler = computationHandler; - } - - // Without deps, always dispose and recreate the computation with every render. - dispose(refs); - track(refs, forceUpdate, (c: Tracker.Computation) => { if (c.firstRun) { - doFirstRun(refs, c); + // Always run the reactiveFn on firstRun + const data = reactiveFn(c); + if (Meteor.isDevelopment) { + checkCursor(data); + } + refs.trackerData = data; } else { // For any reactive change, forceUpdate and let the next render rebuild the computation. forceUpdate(); } - }); + })); // To avoid creating side effects in render with Tracker when not using deps // create the computation, run the user's reactive function in a computation synchronously, @@ -166,7 +78,10 @@ const useTrackerNoDeps: useTrackerSignature = (reactiveFn, deps = null, computat if (!refs.isMounted) { // We want to forceUpdate in useEffect to support StrictMode. // See: https://github.com/meteor/react-packages/issues/278 - dispose(refs); + if (refs.computation) { + refs.computation.stop(); + delete refs.computation; + } } useEffect(() => { @@ -178,79 +93,45 @@ const useTrackerNoDeps: useTrackerSignature = (reactiveFn, deps = null, computat forceUpdate(); // stop the computation on unmount - return () => dispose(refs); + return () =>{ + refs.computation?.stop(); + } }, []); return refs.trackerData; } -const useTrackerWithDeps: useTrackerSignature = (reactiveFn, deps: Array, computationHandler) => { - const { current: refs } = useRef({ - reactiveFn, - isMounted: false, - trackerData: null - }); - const forceUpdate = useForceUpdate(); +const useTrackerClient = (reactiveFn: (c?: Tracker.Computation) => T, deps: DependencyList): T => { + let [data, setData] = useState(); - // Always have up to date deps and computations in all contexts - refs.reactiveFn = reactiveFn; - refs.deps = deps; - if (computationHandler) { - refs.computationHandler = computationHandler; - } - - // We are abusing useMemo a little bit, using it for it's deps - // compare, but not for it's memoization. useMemo(() => { - // stop the old one. - dispose(refs); - - track(refs, forceUpdate, tracked) - - // Tracker creates side effect in render, which can be problematic in some cases, such as - // Suspense or concurrent rendering or if an error is thrown and handled by an error boundary. - // We still want synchronous rendering for a number of reasons (see readme). useTracker works - // around memory/resource leaks by setting a time out to automatically clean everything up, - // and watching a set of references to make sure everything is choreographed correctly. - if (!refs.isMounted) { - // Components yield to allow the DOM to update and the browser to paint before useEffect - // is run. In concurrent mode this can take quite a long time. 1000ms should be enough - // in most cases. - refs.disposeId = setTimeout(() => { - if (!refs.isMounted) { - dispose(refs); - } - }, 1000); + // To jive with the lifecycle interplay between Tracker/Subscribe, run the + // reactive function in a computation, then stop it, to force flush cycle. + const comp = Tracker.nonreactive( + () => Tracker.autorun((c: Tracker.Computation) => { + if (c.firstRun) data = reactiveFn(); + }) + ); + // To avoid creating side effects in render, stop the computation immediately + Meteor.defer(() => { comp.stop() }); + if (Meteor.isDevelopment) { + checkCursor(data); } }, deps); useEffect(() => { - refs.isMounted = true; - - // Render is committed, clear the dispose timeout - clear(refs); - - // If it took longer than 1000ms to get to useEffect, or a reactive update happened - // before useEffect, restart the computation and forceUpdate. - if (!refs.computation) { - // This also runs runReactiveFn - track(refs, forceUpdate, tracked); - forceUpdate(); + const computation = Tracker.autorun((c) => { + setData(reactiveFn(c)); + }); + return () => { + computation.stop(); } + }, deps); - // stop the computation on unmount - return () => dispose(refs); - }, []); - - return refs.trackerData; + return data as T; } -const useTrackerClient: useTrackerSignature = (reactiveFn, deps = null, computationHandler) => - (deps === null || deps === undefined || !Array.isArray(deps)) - ? useTrackerNoDeps(reactiveFn, deps, computationHandler) - : useTrackerWithDeps(reactiveFn, deps, computationHandler); - -const useTrackerServer: useTrackerSignature = (reactiveFn, deps = null, computationHandler) => +const useTrackerServer = (reactiveFn: () => T, deps: DependencyList): T => Tracker.nonreactive(reactiveFn); // When rendering on the server, we don't want to use the Tracker. @@ -259,28 +140,26 @@ const useTracker = Meteor.isServer ? useTrackerServer : useTrackerClient; -const useTrackerDev: useTrackerSignature = (reactiveFn, deps = null, computationHandler) => { +const useTrackerDev = (reactiveFn: () => T, deps: DependencyList): T => { if (typeof reactiveFn !== 'function') { console.warn( 'Warning: useTracker expected a function in it\'s first argument ' + `(reactiveFn), but got type of ${typeof reactiveFn}.` ); } - if (deps && !Array.isArray(deps)) { + if (!Array.isArray(deps)) { console.warn( 'Warning: useTracker expected an array in it\'s second argument ' + `(dependency), but got type of ${typeof deps}.` ); } - if (computationHandler && typeof computationHandler !== 'function') { - console.warn( - 'Warning: useTracker expected a function in it\'s third argument' - + `(computationHandler), but got type of ${typeof computationHandler}.` - ); - } - return useTracker(reactiveFn, deps, computationHandler); + return useTracker(reactiveFn, deps); } export default Meteor.isDevelopment ? useTrackerDev : useTracker; + +export const useTrackerLegacy = Meteor.isServer +? useTrackerServer +: useTrackerNoDeps; diff --git a/packages/react-meteor-data/withTracker.tests.js b/packages/react-meteor-data/withTracker.tests.js index 31f666bd..5f7d44a9 100644 --- a/packages/react-meteor-data/withTracker.tests.js +++ b/packages/react-meteor-data/withTracker.tests.js @@ -141,66 +141,6 @@ if (Meteor.isClient) { completed(); }); - Tinytest.addAsync('withTracker - track based on props and state (with deps)', async function (test, completed) { - var container = document.createElement("DIV"); - - var xs = [new ReactiveVar('aaa'), - new ReactiveVar('bbb'), - new ReactiveVar('ccc')]; - - let setState; - var Foo = (props) => { - const [state, _setState] = useState({ m: 0 }); - setState = _setState; - const Component = withTracker(() => { - return { - x: xs[state.m + props.n].get() - }; - })((props) => { - return {props.x}; - }); - return - }; - - ReactDOM.render(, container); - - test.equal(getInnerHtml(container), 'aaa'); - - xs[0].set('AAA'); - await waitFor(() => { - Tracker.flush({_throwFirstError: true}); - }, { container, timeout: 250 }); - test.equal(getInnerHtml(container), 'AAA'); - - xs[1].set('BBB'); - setState({m: 1}); - await waitFor(() => { - Tracker.flush({_throwFirstError: true}); - }, { container, timeout: 250 }); - test.equal(getInnerHtml(container), 'BBB'); - - setState({m: 2}); - await waitFor(() => { - Tracker.flush({_throwFirstError: true}); - }, { container, timeout: 250 }); - test.equal(getInnerHtml(container), 'ccc'); - xs[2].set('CCC'); - await waitFor(() => { - Tracker.flush({_throwFirstError: true}); - }, { container, timeout: 250 }); - test.equal(getInnerHtml(container), 'CCC'); - - ReactDOM.unmountComponentAtNode(container); - - ReactDOM.render(, container); - setState({m: 0}); - test.equal(getInnerHtml(container), 'AAA'); - - ReactDOM.unmountComponentAtNode(container); - - completed(); - }); - function waitForTracker(func, callback) { Tracker.autorun(function (c) { if (func()) { diff --git a/packages/react-meteor-data/withTracker.tsx b/packages/react-meteor-data/withTracker.tsx index 94862f1c..6cff054a 100644 --- a/packages/react-meteor-data/withTracker.tsx +++ b/packages/react-meteor-data/withTracker.tsx @@ -1,5 +1,5 @@ import React, { forwardRef, memo } from 'react'; -import useTracker from './useTracker'; +import { useTrackerLegacy } from './useTracker'; type ReactiveFn = (props: object) => any; type ReactiveOptions = { @@ -14,7 +14,7 @@ export default function withTracker(options: ReactiveFn | ReactiveOptions) { : options.getMeteorData; const WithTracker = forwardRef((props, ref) => { - const data = useTracker(() => getMeteorData(props) || {}); + const data = useTrackerLegacy(() => getMeteorData(props) || {}); return ( ); diff --git a/packages/react-meteor-state/README.md b/packages/react-meteor-state/README.md new file mode 100644 index 00000000..de03b5c9 --- /dev/null +++ b/packages/react-meteor-state/README.md @@ -0,0 +1,4 @@ +meteor/react-meteor-state +========================= + +A state hook, with an API similar to React's useState, which provides data persistence between page reloads using hte Meteor reload package. diff --git a/packages/react-meteor-state/package-lock.json b/packages/react-meteor-state/package-lock.json new file mode 100644 index 00000000..51ecb4b2 --- /dev/null +++ b/packages/react-meteor-state/package-lock.json @@ -0,0 +1,430 @@ +{ + "name": "react-meteor-state", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz", + "integrity": "sha512-umhPIcMrlBZ2aTWlWjUseW9LjQKxi1dpFlQS8DzsxB//5K+u6GLTC/JliPKHsd5kJVPIU6X/Hy0YvWOYPcMxBw==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@jest/types": { + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.5.2.tgz", + "integrity": "sha512-QDs5d0gYiyetI8q+2xWdkixVQMklReZr4ltw7GFDtb4fuJIBCE6mzj2LnitGqCuAlLap6wPyb8fpoHgwZz5fdg==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@testing-library/dom": { + "version": "7.26.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.26.0.tgz", + "integrity": "sha512-fyKFrBbS1IigaE3FV21LyeC7kSGF84lqTlSYdKmGaHuK2eYQ/bXVPM5vAa2wx/AU1iPD6oQHsxy2QQ17q9AMCg==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.10.3", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.1", + "lz-string": "^1.4.4", + "pretty-format": "^26.4.2" + } + }, + "@testing-library/react": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz", + "integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==", + "requires": { + "@babel/runtime": "^7.10.3", + "@testing-library/dom": "^7.22.3" + } + }, + "@types/aria-query": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==" + }, + "@types/bson": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.2.tgz", + "integrity": "sha512-+uWmsejEHfmSjyyM/LkrP0orfE2m5Mx9Xel4tXNeqi1ldK5XMQcDsFkBmLDtuyKUbxj2jGDo0H240fbCRJZo7Q==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/meteor": { + "version": "1.4.57", + "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-1.4.57.tgz", + "integrity": "sha512-Kng//SdSaDXnwXmUyUb45e/as3bneaoanTHDqEm1R9gcijqiXz0Gofw0+RaE0U3dB5ej5w2JnUuLR90P+JAhcA==", + "requires": { + "@types/connect": "*", + "@types/mongodb": "*", + "@types/react": "*", + "@types/underscore": "*" + } + }, + "@types/mongodb": { + "version": "3.5.28", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.28.tgz", + "integrity": "sha512-wSbda4QQ3QFsWUHJzC5TSYImtRDtPhPb+lW7ICMtWvvtQMQRXbVTz/Z1P5A3XujK5bZIAV7vk5CXQG6Adz7+Cw==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.11.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.11.10.tgz", + "integrity": "sha512-yV1nWZPlMFpoXyoknm4S56y2nlTAuFYaJuQtYRAOU7xA/FJ9RY0Xm7QOkaYMMmr8ESdHIuUb6oQgR/0+2NqlyA==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz", + "integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/underscore": { + "version": "1.10.24", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz", + "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w==" + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + }, + "dom-accessibility-api": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "pretty-format": { + "version": "26.5.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.5.2.tgz", + "integrity": "sha512-VizyV669eqESlkOikKJI8Ryxl/kPpbdLwNdPs2GrbQs18MpySB5S0Yo0N7zkg2xTRiFq4CFw8ct5Vg4a0xP0og==", + "requires": { + "@jest/types": "^26.5.2", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", + "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-test-renderer": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", + "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==" + } + } +} diff --git a/packages/react-meteor-state/package.js b/packages/react-meteor-state/package.js new file mode 100644 index 00000000..df1b9bff --- /dev/null +++ b/packages/react-meteor-state/package.js @@ -0,0 +1,17 @@ +/* global Package */ + +Package.describe({ + name: 'react-meteor-state', + summary: 'React hook for reactively tracking Meteor data', + version: '0.9.0', + documentation: 'README.md', + git: 'https://github.com/meteor/react-packages', +}); + +Package.onUse(function (api) { + api.versionsFrom('1.10'); + api.use('tracker'); + api.use('typescript'); + + api.mainModule('use-meteor-state.ts', ['client', 'server'], { lazy: true }); +}); diff --git a/packages/react-meteor-state/package.json b/packages/react-meteor-state/package.json new file mode 100644 index 00000000..14b0269d --- /dev/null +++ b/packages/react-meteor-state/package.json @@ -0,0 +1,15 @@ +{ + "name": "meteor-react-meteor-state", + "scripts": { + "make-types": "npx typescript *.ts --declaration --emitDeclarationOnly --outDir types" + }, + "dependencies": { + "@testing-library/react": "^10.0.2", + "@types/meteor": "^1.4.42", + "@types/react": "^16.9.34", + "react": "16.13.1", + "react-dom": "16.13.1", + "react-test-renderer": "16.13.1", + "typescript": "^4.0.3" + } +} diff --git a/packages/react-meteor-state/types/use-meteor-state.d.ts b/packages/react-meteor-state/types/use-meteor-state.d.ts new file mode 100644 index 00000000..05ea1912 --- /dev/null +++ b/packages/react-meteor-state/types/use-meteor-state.d.ts @@ -0,0 +1,3 @@ +import { SetStateAction, Dispatch } from 'react'; +declare const _default: (initialValue: S | (() => S), name: string) => [S, Dispatch>]; +export default _default; diff --git a/packages/react-meteor-state/use-meteor-state.ts b/packages/react-meteor-state/use-meteor-state.ts new file mode 100644 index 00000000..fcda7a47 --- /dev/null +++ b/packages/react-meteor-state/use-meteor-state.ts @@ -0,0 +1,47 @@ +import { Meteor } from 'meteor/meteor' +import { Reload } from 'meteor/reload' +import { useState, useEffect, SetStateAction, Dispatch } from 'react' + +interface IHash { + [key: string] : any +} + +if (Meteor.isClient) { + var toMigrate: IHash = {} + var migrated: IHash = Reload._migrationData('use-meteor-state') || {} + + Reload._onMigrate('use-meteor-state', () => [true, toMigrate]) +} + +const useMeteorState = (initialValue: S | (() => S), name: string): [S, Dispatch>] => { + // When running in concurrent mode, this may run multiple times ... + if (migrated[name]) { + initialValue = migrated[name] + } + + useEffect(() => { + // ... so cleanup happens only after the render is committed + if (migrated[name]) { + // move to toMigrate for next refresh + toMigrate[name] = migrated[name] + delete migrated[name] + } + return () => { + // Remove migration on unmount + if (toMigrate[name]) { + delete toMigrate[name] + } + } + }, [name]) + + const [value, setValue] = useState(initialValue) + + return [value, (value: S) => { + toMigrate[name] = value + setValue(value) + }] +} + +export default Meteor.isClient + ? useMeteorState + : useState diff --git a/packages/react-mongo/README.md b/packages/react-mongo/README.md new file mode 100644 index 00000000..1877e6fd --- /dev/null +++ b/packages/react-mongo/README.md @@ -0,0 +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
+ { + subscription.ready() + ? 'content ready' + : 'loading...' + } +
+} +``` diff --git a/packages/react-mongo/package-lock.json b/packages/react-mongo/package-lock.json new file mode 100644 index 00000000..4647c678 --- /dev/null +++ b/packages/react-mongo/package-lock.json @@ -0,0 +1,430 @@ +{ + "name": "react-mongo", + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "dependencies": { + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + } + } + }, + "@babel/runtime": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.12.1.tgz", + "integrity": "sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/runtime-corejs3": { + "version": "7.12.1", + "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.12.1.tgz", + "integrity": "sha512-umhPIcMrlBZ2aTWlWjUseW9LjQKxi1dpFlQS8DzsxB//5K+u6GLTC/JliPKHsd5kJVPIU6X/Hy0YvWOYPcMxBw==", + "requires": { + "core-js-pure": "^3.0.0", + "regenerator-runtime": "^0.13.4" + } + }, + "@jest/types": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.0.tgz", + "integrity": "sha512-8pDeq/JVyAYw7jBGU83v8RMYAkdrRxLG3BGnAJuqaQAUd6GWBmND2uyl+awI88+hit48suLoLjNFtR+ZXxWaYg==", + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^15.0.0", + "chalk": "^4.0.0" + } + }, + "@testing-library/dom": { + "version": "7.26.3", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.26.3.tgz", + "integrity": "sha512-/1P6taENE/H12TofJaS3L1J28HnXx8ZFhc338+XPR5y1E3g5ttOgu86DsGnV9/n2iPrfJQVUZ8eiGYZGSxculw==", + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.10.3", + "@types/aria-query": "^4.2.0", + "aria-query": "^4.2.2", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.1", + "lz-string": "^1.4.4", + "pretty-format": "^26.4.2" + } + }, + "@testing-library/react": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-10.4.9.tgz", + "integrity": "sha512-pHZKkqUy0tmiD81afs8xfiuseXfU/N7rAX3iKjeZYje86t9VaB0LrxYVa+OOsvkrveX5jCK3IjajVn2MbePvqA==", + "requires": { + "@babel/runtime": "^7.10.3", + "@testing-library/dom": "^7.22.3" + } + }, + "@types/aria-query": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==" + }, + "@types/bson": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/bson/-/bson-4.0.3.tgz", + "integrity": "sha512-mVRvYnTOZJz3ccpxhr3wgxVmSeiYinW+zlzQz3SXWaJmD1DuL05Jeq7nKw3SnbKmbleW5qrLG5vdyWe/A9sXhw==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.33", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", + "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", + "requires": { + "@types/node": "*" + } + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz", + "integrity": "sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw==" + }, + "@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz", + "integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==", + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/meteor": { + "version": "1.4.60", + "resolved": "https://registry.npmjs.org/@types/meteor/-/meteor-1.4.60.tgz", + "integrity": "sha512-NsuIIKtGABovJHrE2H0+PUDlGTuvCL3UjX9fgxJOk43oRzmA+1FMOnGz4n1n9J6G6vbw9PumdWZOWTZkH/NnRw==", + "requires": { + "@types/connect": "*", + "@types/mongodb": "*", + "@types/react": "*", + "@types/underscore": "*" + } + }, + "@types/mongodb": { + "version": "3.5.29", + "resolved": "https://registry.npmjs.org/@types/mongodb/-/mongodb-3.5.29.tgz", + "integrity": "sha512-aFEMjwSMJ6pstVDwDP4k7cys1iCbAGL01mAp5opOdbK9jv6JmwvYgpGje22Mp2HtrKq5Seea+5ti7CQ/Ovyw2Q==", + "requires": { + "@types/bson": "*", + "@types/node": "*" + } + }, + "@types/node": { + "version": "14.14.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.0.tgz", + "integrity": "sha512-BfbIHP9IapdupGhq/hc+jT5dyiBVZ2DdeC5WwJWQWDb0GijQlzUFAeIQn/2GtvZcd2HVUU7An8felIICFTC2qg==" + }, + "@types/prop-types": { + "version": "15.7.3", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + }, + "@types/react": { + "version": "16.9.53", + "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.53.tgz", + "integrity": "sha512-4nW60Sd4L7+WMXH1D6jCdVftuW7j4Za6zdp6tJ33Rqv0nk1ZAmQKML9ZLD4H0dehA3FZxXR/GM8gXplf82oNGw==", + "requires": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, + "@types/underscore": { + "version": "1.10.24", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.10.24.tgz", + "integrity": "sha512-T3NQD8hXNW2sRsSbLNjF/aBo18MyJlbw0lSpQHB/eZZtScPdexN4HSa8cByYwTw9Wy7KuOFr81mlDQcQQaZ79w==" + }, + "@types/yargs": { + "version": "15.0.9", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.9.tgz", + "integrity": "sha512-HmU8SeIRhZCWcnRskCs36Q1Q00KBV6Cqh/ora8WN1+22dY07AZdn6Gel8QZ3t26XYPImtcL8WV/eqjhVmMEw4g==", + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", + "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==" + }, + "ansi-regex": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", + "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "aria-query": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-4.2.2.tgz", + "integrity": "sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==", + "requires": { + "@babel/runtime": "^7.10.2", + "@babel/runtime-corejs3": "^7.10.2" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + }, + "core-js-pure": { + "version": "3.6.5", + "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz", + "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==" + }, + "csstype": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", + "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + }, + "dom-accessibility-api": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lz-string": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "pretty-format": { + "version": "26.6.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.0.tgz", + "integrity": "sha512-Uumr9URVB7bm6SbaByXtx+zGlS+0loDkFMHP0kHahMjmfCtmFY03iqd++5v3Ld6iB5TocVXlBN/T+DXMn9d4BA==", + "requires": { + "@jest/types": "^26.6.0", + "ansi-regex": "^5.0.0", + "ansi-styles": "^4.0.0", + "react-is": "^16.12.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, + "react": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", + "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2" + } + }, + "react-dom": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", + "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "scheduler": "^0.19.1" + } + }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "react-test-renderer": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-test-renderer/-/react-test-renderer-16.13.1.tgz", + "integrity": "sha512-Sn2VRyOK2YJJldOqoh8Tn/lWQ+ZiKhyZTPtaO0Q6yNj+QDbmRkVFap6pZPy3YQk8DScRDfyqm/KxKYP9gCMRiQ==", + "requires": { + "object-assign": "^4.1.1", + "prop-types": "^15.6.2", + "react-is": "^16.8.6", + "scheduler": "^0.19.1" + } + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "scheduler": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", + "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", + "requires": { + "loose-envify": "^1.1.0", + "object-assign": "^4.1.1" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "typescript": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", + "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==" + } + } +} diff --git a/packages/react-mongo/package.js b/packages/react-mongo/package.js new file mode 100644 index 00000000..e62fb58d --- /dev/null +++ b/packages/react-mongo/package.js @@ -0,0 +1,17 @@ +/* global Package */ + +Package.describe({ + name: 'react-mongo', + summary: 'React hook for reactively tracking Meteor data', + version: '0.9.0', + documentation: 'README.md', + git: 'https://github.com/meteor/react-packages', +}); + +Package.onUse(function (api) { + api.versionsFrom('1.10'); + api.use('tracker'); + api.use('typescript'); + + api.mainModule('react-mongo.ts', ['client', 'server'], { lazy: true }); +}); diff --git a/packages/react-mongo/package.json b/packages/react-mongo/package.json new file mode 100644 index 00000000..6b836a62 --- /dev/null +++ b/packages/react-mongo/package.json @@ -0,0 +1,15 @@ +{ + "name": "meteor-react-mongo", + "scripts": { + "make-types": "npx typescript *.ts --declaration --emitDeclarationOnly --outDir types" + }, + "dependencies": { + "@testing-library/react": "^10.0.2", + "@types/meteor": "^1.4.42", + "@types/react": "^16.9.34", + "react": "16.13.1", + "react-dom": "16.13.1", + "react-test-renderer": "16.13.1", + "typescript": "^4.0.3" + } +} diff --git a/packages/react-mongo/react-mongo.ts b/packages/react-mongo/react-mongo.ts new file mode 100644 index 00000000..12534c3b --- /dev/null +++ b/packages/react-mongo/react-mongo.ts @@ -0,0 +1,193 @@ +import { Meteor } from 'meteor/meteor' +import { Mongo } from 'meteor/mongo' +import { Tracker } from 'meteor/tracker' +import { EJSON } from 'meteor/ejson' +import { useEffect, useReducer, useRef, DependencyList, Reducer, useMemo } from 'react' + +const fur = (x: number): number => x + 1 +const useForceUpdate = () => useReducer(fur, 0)[1] + +type useSubscriptionRefs = { + subscription?: Meteor.SubscriptionHandle, + updateOnReady: boolean, + isReady: boolean, + params: { + name?: string, + args: any[] + } +} + +const useSubscriptionClient = (name?: string, ...args: any[]): [() => boolean, Meteor.SubscriptionHandle | undefined] => { + const forceUpdate = useForceUpdate() + + const refs: useSubscriptionRefs = useRef({ + updateOnReady: false, + isReady: false, + params: { + name, + args + } + }).current + + if (!EJSON.equals(refs.params, { name, args })) { + refs.updateOnReady = false + refs.isReady = false + refs.params = { name, args } + } + + useEffect(() => { + const computation = Tracker.nonreactive(() => ( + Tracker.autorun(() => { + const { name, args } = refs.params + if (!name) return + + refs.subscription = Meteor.subscribe(name, ...args) + + const isReady = refs.subscription.ready() + if (isReady !== refs.isReady) { + refs.isReady = isReady + if (refs.updateOnReady) { + forceUpdate() + } + } + }) + )) + + return () => { + computation.stop() + delete refs.subscription + } + }, [refs.params]) + + return [ + () => { + refs.updateOnReady = true + return !refs.isReady + }, + refs.subscription + ] +} + +const useSubscriptionServer = (name?: string, ...args: any[]): [() => boolean, Meteor.SubscriptionHandle | undefined] => ([ + () => false, + undefined +]) + +export const useSubscription = Meteor.isServer + ? useSubscriptionServer + : useSubscriptionClient + +type useFindActions = + | { type: 'refresh', data: T[] } + | { type: 'addedAt', document: T, atIndex: number } + | { type: 'changedAt', document: T, atIndex: number } + | { type: 'removedAt', atIndex: number } + | { type: 'movedTo', fromIndex: number, toIndex: number } + +const useFindReducer = (data: T[], action: useFindActions): T[] => { + switch (action.type) { + case 'refresh': + return action.data + case 'addedAt': + return [ + ...data.slice(0, action.atIndex), + action.document, + ...data.slice(action.atIndex) + ] + case 'changedAt': + return [ + ...data.slice(0, action.atIndex), + action.document, + ...data.slice(action.atIndex + 1) + ] + case 'removedAt': + return [ + ...data.slice(0, action.atIndex), + ...data.slice(action.atIndex + 1) + ] + case 'movedTo': + const doc = data[action.fromIndex] + const copy = [ + ...data.slice(0, action.fromIndex), + ...data.slice(action.fromIndex + 1) + ] + copy.splice(action.toIndex, 0, doc) + return copy + } +} + +const checkCursor = (cursor: Mongo.Cursor) => { + if (!(cursor instanceof Mongo.Cursor)) { + console.warn( + 'Warning: useFind requires an instance of Mongo.Cursor. ' + + 'Make sure you do NOT call fetch() on your cursor.' + ); + } +} + +const useFindClient = (factory: () => Mongo.Cursor, deps: DependencyList) => { + let [data, dispatch] = useReducer>>( + useFindReducer, + [] + ) + + const cursor = useMemo(() => ( + // To avoid creating side effects in render, opt out + // of Tracker integration altogether. + Tracker.nonreactive(() => { + const c = factory() + if (Meteor.isDevelopment) { + checkCursor(c) + } + data = c.fetch() + return c + }) + ), deps) + + useEffect(() => { + // Refetch the data in case an update happened + // between first render and commit. Additionally, + // update in response to deps change. + const data = Tracker.nonreactive(() => cursor.fetch()) + + dispatch({ + type: 'refresh', + data: data + }) + + const observer = cursor.observe({ + addedAt (document, atIndex, before) { + dispatch({ type: 'addedAt', document, atIndex }) + }, + changedAt (newDocument, oldDocument, atIndex) { + dispatch({ type: 'changedAt', document: newDocument, atIndex }) + }, + removedAt (oldDocument, atIndex) { + dispatch({ type: 'removedAt', atIndex }) + }, + movedTo (document, fromIndex, toIndex, before) { + dispatch({ type: 'movedTo', fromIndex, toIndex }) + }, + // @ts-ignore + _suppress_initial: true + }) + + return () => { + observer.stop() + } + }, [cursor]) + + return data +} + +const useFindServer = (factory: () => Mongo.Cursor, deps: DependencyList) => ( + Tracker.nonreactive(() => { + const cursor = factory() + if (Meteor.isDevelopment) checkCursor(cursor) + return cursor.fetch() + }) +) + +export const useFind = Meteor.isServer + ? useFindServer + : useFindClient diff --git a/packages/react-mongo/types/react-mongo.d.ts b/packages/react-mongo/types/react-mongo.d.ts new file mode 100644 index 00000000..c4d38593 --- /dev/null +++ b/packages/react-mongo/types/react-mongo.d.ts @@ -0,0 +1,5 @@ +import { Meteor } from 'meteor/meteor'; +import { Mongo } from 'meteor/mongo'; +import { DependencyList } from 'react'; +export declare const useSubscription: (name?: string, ...args: any[]) => [() => boolean, Meteor.SubscriptionHandle | undefined]; +export declare const useFind: (factory: () => Mongo.Cursor, deps: DependencyList) => T[];