{"_id":"react-recollect","_rev":"62-6f2859ae02400f6a79eafd30e2f6fa82","name":"react-recollect","dist-tags":{"latest":"5.2.3"},"versions":{"1.0.0":{"name":"react-recollect","version":"1.0.0","description":"A library for managing data within a React app.","main":"lib/index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","dependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","react":"^16.6.0"},"gitHead":"9d75ce055448ab39aedd0e540ceba2f208b3c357","_id":"react-recollect@1.0.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-4d5h761llFc7Qb0KWag25FsmLqwUTPqPXAjY6QTClgx8Iuf4a4Aa5OWEa3RYHaBk5yZd1yKxTPOhyYRjDx+l4A==","shasum":"077d5f361676e931d77d7d8419a23c1b4eb81fa8","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.0.0.tgz","fileCount":5,"unpackedSize":13979,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb15UqCRA9TVsSAnZWagAA6vAQAJO3WQjtCZLj5RZ9W3gg\nfSqkJKz5QaIS4fWoIpN5zE8zP3z3TejB8T89rcLTLZhzl9+Xb9CC0P19ROFz\nVg5+045LK2hgxPLsUzD/yH8oXJYlEHMZGwXW/lZMtS1amAq7+572t8hAwDQr\nJVrSRTebsISHutbVycKUOnphy/OdisPsDxpqqk7OkbOGr//X6gLC7OEIDcZy\nzDT1Ql5Ioder665cSuIpMMAAWJL97U4EQCKfISr4yNririTKuvAxYl4QdCVV\nwG2m5hkNC2eqgfCLgD6/4hmo8dwAC1TKxR7xcPSXIU1OdHpousdSZyJCir18\nVbOxuS4dJyzZh75jwo8vIw41rP1S+rXbDazHCvYUlMx2gZWVro+1lvs2wE4T\np0BbB4F3Sw0l9cO1zahBUHOwHZXXY5OWj2Xa/pkJ2BDI/PIy0Mlh3Tygb5y4\n6D26JlMsOdOxlWcVQBxu06P3zKCZZfVW8RHq7Vil8TNf17wbTXAtRfDqXmwc\nn2LbgKPC500N6f91ztyNNS+/URl/gKuV8lLza581KOJ00X6hv8yP5ZgRwP2q\n3VUB7aWnMfoH0JZgoyBcby/mJ2NgckJMSUcwTzuzSrfB505/TS9ORe+nzODO\nnyS5U+ZZRw8AqR+0XZdYJNwVtEBq4UaBDhVprw3gsSWVN+zwGrwD3MQyydfu\nXnoQ\r\n=qMxk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAq0F7UiKej1D1A2iB62mYMMMn+UlAi8MOz8Xd2GwGFqAiEA5T3x+9Fv4iCANntkhXrBE2QxqsceuyUWw2Mc5Irzxv0="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.0.0_1540855081405_0.5913202233411017"},"_hasShrinkwrap":false},"1.0.1":{"name":"react-recollect","version":"1.0.1","description":"A library for managing data within a React app.","main":"lib/index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-react":"^7.0.0","react":"^16.6.0"},"gitHead":"4f8119e609382187201a05f4b598b6594da0d74d","_id":"react-recollect@1.0.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-srFbOSXzYfx/zUlGO5XlRRv103O95bNaKxRQEEj4KeYBZHgoGY+i4h00Xbb/kuRmYbazUuoTW35kzrcRiqwd2A==","shasum":"d184b4d171f3f83558854437a1171c1cd1fcac0e","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.0.1.tgz","fileCount":5,"unpackedSize":14142,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb15t2CRA9TVsSAnZWagAAmb8P/1wGk1usRtVGyniU3NAx\nFG/vZPJszc1y2Hr0oDSB1k3h1oK4OsZGCCH3aSwkQvv187kSjuW4qv41WRT0\n66A9YKPRrWdHv0g2Jfex2vgWXNW40CWd26XeSB2LCdyYO889RfsbsYk+DZuI\nhL0k81Ze3OXEwDIK1gMUlqAmZrT3gawcKP4XDIwmY3UmKZAjfsYrrqS20Op+\n3TDZgC9io/S+nHpFZhSVx1qdChVBko10MimoO7/KwdaMRJvfNwyk4EjBEee/\nKIl9zuWwGfG3Zp+eqHStAVeXCDxavmtX8QglYpgE4WlrFc+jWMeuyK8BXh3w\nmw9PZwE4BLPUMVwUy+YEWwAtYVlkJbud74ZNgn+YZFdIk6DWgWVzyRcrHN1K\n8LlRRbRJD7woamEsq8iBzXxoP/edpOEDfeVMwfpoQF3Jjlcs+UpaK/w4CId6\nCuZm7RfU+YIjUm3KpHZCzmwRkrOrcOW8iq90XmehTTIlpSaXyfqlGDM+xGwj\nwY5auITwY17jTy7+Y5LS1wpxq2lwUapD2bBWXtsXz32cMV3CCECHWP+6A/hM\n8QH/OYwbaK8lLKdNg7G+zhuG/aS1Dvfo2mU60uE4iw3mCrlzuQ+o401NZZJP\n5tNz8CDbI/mZx20y//DZu/TRDfFmDNYWxYPsk1zovys2vDdk1CT25GlG+1qs\nGF3u\r\n=HOfX\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIE2+9Fic7MKxni0FO69hxUqyYR3oaVZuRSIyDbwHA3l9AiBewYkkFlhyb10FTqT3jxxJI6HOuhTAbXSlMPCKpjVEOA=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.0.1_1540856694106_0.12894822597298927"},"_hasShrinkwrap":false},"1.1.0":{"name":"react-recollect","version":"1.1.0","description":"A library for managing data within a React app.","main":"lib/index.js","scripts":{"test":"echo \"Error: no test specified\" && exit 1","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-react":"^7.0.0","react":"^16.6.0"},"gitHead":"c631005bebc9641266aa90f77d171db1568dfbe2","_id":"react-recollect@1.1.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-9mpEMkl25XRuhMZD+XqmZkguthVsPfxBJuEmXIckGCMdZ4II9gHZ365auffpkVdIF5fZhfWaIG6IGF+H+BrXHg==","shasum":"8cd02e07c03d0d775295dc0c65540debbc1837de","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.1.0.tgz","fileCount":5,"unpackedSize":14175,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb18X3CRA9TVsSAnZWagAArEMQAIZ357mR6CLAq0Brrrzj\njNKPtCofPm7JZTB7pu5FR3z0LnSOhs2H2Naji25gqOND7J7SrXvd+tzpiImU\nd2yBrb+Q50R12vLoqsYUwS7sKcqPvkUFKmNMsr+ebp3qSM+/Xx3E0hz5NsvP\nJ8iW0Q67g+mwM51oXk+fap93zcNqIEt3zUP9/kpYUMxUhBDO+kbRaiyQTj56\nwR1oK4agPvQS19TI1eLoRm26bNkhWFeoF5j/FzoHpW7gyP7drfrvrx5WyTpu\n2GDBeysylMPJw4MAwBaosN7wUNtfRy1UzFghiLJrxXlXKBJ1SLAOfaqtHT1C\n3/aTpEbt2oDeV0wpVtKHm7PntaRz5uW1gwb+hOwSmumynKF3enjFxFeXtVNT\nE2rBWIc9nmD25lzGxijTv18ND+v0Rsji8sosyT1GBE/zuoIVUU7cSd8clAM9\nFVOPfYsWneIiR8xikSNkyQofNWyBS2bJIfBJrezFFsBdc6+MLKH8LF9KVhoU\nLEXtp0gjY4PXjyVJSlKeE3Q/1VGKPhvYFAr42j0yjoc1wq6jqXIvla2PQx7K\nD/SOTUj388giu7uEkQCxzgiBiTCxZ0PQ48IISM1mS4DbsvOB99iNyL6iliwn\nHQxggjKxXNgxlwqusbVnBMvIp9PFfRFoPFvvv3OK/reHxDMkM6Fijx800MtK\nxXNR\r\n=BQpI\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCJKO/d6mSVQ29PbjBbGf9aY6FAPntLGJAZkV1VRC5mvAIhAM6cofTK9NpCtugagRzdHfTYVMBAIBKRFy+eGVwmTmjO"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.1.0_1540867574877_0.10913849176133672"},"_hasShrinkwrap":false},"1.1.1":{"name":"react-recollect","version":"1.1.1","description":"Featureless state management for React.","main":"lib/index.js","scripts":{"prepublish":"npm run build","test":"echo \"Error: no test specified\" && exit 1","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-react":"^7.0.0","react":"^16.6.0"},"gitHead":"8f44f749e9c513fff96da1db24d9b14a7d72ab66","_id":"react-recollect@1.1.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-9s4ysk013Zu6JwcsWACjAFbBWDah744PvwS1WU7ZeBcXm7uopLjZDXJxNpvreMkBetfk0u9HL7YMftf3kfBcWg==","shasum":"1a63b6a7fb5aea28feafdd3582ca05a5eaba91e3","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.1.1.tgz","fileCount":5,"unpackedSize":16498,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb274UCRA9TVsSAnZWagAAXzsQAJ+nXyamc6iQiY0J0ROk\nvk1Nqoz3stQLuSA5VgT6VYkQFj4rzkteG+De5AhhL4VLrV9bmDYUMFWvCv4+\n2c19EnIGmHUTUjc3wUFqmsALIPRpkYan/7Zx9elpntCJsszT1pASVO2LRHwC\ns2gU9RfEHf0nh1oapv8ClGJJJiUzuUScRnotsXrQaxR26oMQzLzxYsHue/F7\nKr7vexBQvXxSH1FZ9eaj8yYz294peRODfnK/eFr5QqJa/JRtO6x5PL3w1YL7\ny4F+qh+V2eQFtB3vE4+f1mkLHWimcsjNlSatjpbx8TyBp5m9pwGL/P2GqMzN\nJcLxpR1FRg5aoa350PCa8D6jf4vy63dOiT2Bg1cxfhHlAgx0auBrsY8GQ+IB\nxUqbJpxnrqCZhWXdMkIh1XUPossj5yFHrQyI3l/pOmu0PvQhl8K85Xg3hyTJ\ntzJ99+jWADt/pRY8eoZkM2C4hIfx+/x4Da9gn4cLPAJPgcZBF6ZfKtNfk+QT\n93VeesWS2ouuP7JtJsa/JLzSRosw8Y6yJLedVafUH1YfiocIs7v8a0fLs+KK\nc7R+R0Un+XL0DJ6WyWrmpD7YE9dmxVaMpAwIgPKgAoXi9zJy4t1UODuIfksu\nwGKAy7NEMioxcDGaR91WfE2gM7pJkEzbvZmhoaA0kX9g6wP0A/gkvTs6IuAB\nd7kv\r\n=uoJn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCID1M+ZUjNTy54BaE0YzHZ1zPMCP16qFDInTfkpDLUL4iAiEA/Pdgzu1rsXLmfHBN/5AiwG692Qa3SdOS5z9VrnlNe9w="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.1.1_1541127700327_0.8499224752073669"},"_hasShrinkwrap":false},"1.1.2":{"name":"react-recollect","version":"1.1.2","description":"**Featureless state management for React.**","main":"lib/index.js","scripts":{"preversion":"npm run build","test":"jest --watch","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"644d2bcac8d9b0da78bffe256dc60c6d231c7215","_id":"react-recollect@1.1.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-CDY78caitRw1G2DRoaUjPRj0RK+j2nXoMFv9m7KBbeO1CMCrOWqPCCIGfvOeO0MJF56R0CE53noSueso+absEA==","shasum":"fb9bbc06ac8b2336ac19e0a87e3249b197f8553e","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.1.2.tgz","fileCount":11,"unpackedSize":39367,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb3mjECRA9TVsSAnZWagAABucP/R76bQQ3zLbjEmA9AjsZ\nWYhUlPBtmSLmsA+LZ3EVRSNBGUZTW6anExgj6e/FCJQyyv8MF9glVE2HjTKW\naCWUxGq+bFE16153GwTHX3mugFfQtWWZeI0C6U/gd7dj756ZNQSZe0Tfjwzs\nEDJtdf5u0NvKNP/PDqz7kccxsyAJbsK6aC/R/KcQc7BcMiL+riWZuT/3sLFh\nRRaEBJ5NDhf7nRBcuNz+YpYfdOdiwAz4HiqzB4MptfO7WJdEHNLZZs7TTDmP\nTrBAikeaCPkvov/fXAdEC4c6alR1/4r8zPYbqpIWTjufuNklBt36WDnUMG1h\nrx7Oz51xUiYdofrBk5mA0DzUTLXPdTlABFjlqUXu7xeXlXKvgDyx3ZlnPtEN\nrjV+0N1mtEttakcL34u1Ca6wH6xjfTDUVfRxZpnCqusfSfhuyuF4FE4PMZLY\np7D8GK9bNx0HxmfP94qrolY46zRWSaHvWATjn9VNY6+Ju9zjiRUBdZojx/xo\necTv82r0IMtlENR4slwxIuSlUCephy+I3ZWqY5Pbl5Q7s37tuGNkS/BtLb/k\nHKRK2YYIWyiB78kDnLLP3KUUuOuMSm31V8aEIkUwkAqD+bj1vy7HOewvDvBT\nF360kOJsQ/XUBUw1Twvsy/nYDD/uEdSkY/kKM6wKFyJlKhURqXw2FGwWCO+m\nzqNI\r\n=iuTw\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC0OnhZnN/YUNDYNxdxuRFeB6VmTB0Zyq8N1fCUpg+eQQIhAKHd1uBmXX/6ZyM1hhP7sNmYJ1M0+NmXA3U6rTBG/ABK"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.1.2_1541302467628_0.5368884501438089"},"_hasShrinkwrap":false},"1.1.3":{"name":"react-recollect","version":"1.1.3","description":"**Featureless state management for React.**","main":"lib/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d lib"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"8ed637a833c515b9ed0e3ec4c0d191d245163207","_id":"react-recollect@1.1.3","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-NrjyY+GPQ2rfMEPW/tg0gnDkGKm3xbEnr0JCluqY+ihkSPxXHKUItiRaoVzxARiCC1yA5MN/wBMy2RyF6nuZ1g==","shasum":"05e47b8ef76fa617b3f57198e63907581a6fbfbb","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-1.1.3.tgz","fileCount":11,"unpackedSize":39665,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb3m1zCRA9TVsSAnZWagAASo8P/0ICdSw/e7ehe1Rb5mRe\nN7V4+RCL2gbt1t3sOkWdriC0dpJKFHLS8AEKTDNPZqfYgiiz/FYvrq4+XN/n\n68kWxwb0YCcK+sfsBpSid7uMxnDf86kPVz81wzyhE0AcP5ps4l8EgmGF00kJ\n0J6wNxARtkf3ns6AOdKxrSLCGp7dTBfoWPp5r4NUH3n+B7M2AEEK0QG327fd\nsCsYdsLBLrryFuDC9vL9vK3gQSi+eZX7Uv0eGwLf35KVQDRAA70dPlLk9dv9\nhCM3LqcQhWn4zR3g0PNu8bWn2GE2elbY3PRFmUULNVLvIfUorjv7V+CCFxsA\nuYfqE24lmXJTLn1k9qGUVVCgfuxxLhQFb20zzfaKFaaJZCA72HuG0N2LppVb\nreSnDHOAsx5beDtMVTteGv/61Yj0ytOijHWDp5na7RIlSIE0Cpdyz/4G9VR0\noTUphawxM/oB8hRBlZK4dnbHPcQk2woQzUB4GuaMxIylL26cpWG1warooT1W\n0eIizmQo7E6dCYTXtEfAgRhGeFd9/vAhrze//Ovsh9yuclD53LU9WVGs1pt1\n8Wnq+iBwseFLx3IaahTiDZJ7tuTQKHkwKxpXCyJVrh82RINGmpfRSELd7fVk\nW+ze4J4CsKaLXt2eK6Fi3gIN5DVzyMGx9yoKD+g+ukO6f+5ZBDP5oQY61sn1\nYWMl\r\n=lGN8\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDs3WwW73DeCF22pHXvFE8aDPRZ2p3xEpU49RcZFhTlywIgCTX2IRuk8UGKCwfFeznZPZT1OD/clLHxZlqU1XYEYuo="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_1.1.3_1541303666519_0.18418158400739149"},"_hasShrinkwrap":false},"2.0.0":{"name":"react-recollect","version":"2.0.0","description":"**Featureless state management for React.**","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"d664b0af14a7a3b6b8dcbabba425ea8d9ac10fbe","_id":"react-recollect@2.0.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-Bo7unqWBt9PzAmgN0S0oXsPXlaXet5UUYfqcC7Sizqq4uScJjUInBohCG+Lw1GV+HA51eoYK2p2+LS5bUcUUQg==","shasum":"8e76683a49eadb62c5dedb909afbb4437fa33abc","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.0.0.tgz","fileCount":28,"unpackedSize":60782,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb59//CRA9TVsSAnZWagAAghcP/0yDokREeyNAjHhm+7I1\ndqEz0tHKvQNFi1T8ZWEYb2DtFZmVRrz9ftQf99IJPqOjOzWN3CXH0sIxL1nx\nCuKBoqEGANPhyqyWS7P3T9n2tHnghy6eAsj3tXqDcbxDM5AZ+d0xYANYozEC\n8Nyh35VIouWgw4souXspARsn73QGPAf08RZmCNq9aZDQvPZQoiJqMucA/kEv\niHi3ONihqgyIXMNWxzLeb1ocbIi29P+5dr8GkM9uXbAghvYiRDsX79i/51h1\nXO55XGcTuYMOUetOcp5Tb/js4lExizODiFGENO90S4WZjPqbeFZ0glOMSPSa\nArPJ47RXK3R/dkwf2xo6D4+yTfGpmXKhf/ADATI3dKnIKR4stCdHoLXrzYkj\nVymTxRgz3WpRVuQL9753NnrCtoPRkP1FI7fuTkJiboMCVmZbdpvTCu+MA2C4\nqMneo7H13drdvloVIJDlwl7IXPHepGm7UgkMi3qHLNgKx+5pvoyXPS+5U5W3\nGXp54uAeAJCTOq9YZgWcnqiOHhUpvl64FP4ZGrqzweI6Fmbq3Hp+uMLmrN66\niRodU/Qy/mmrbDNPvo0AvxwTIPFU2yb2PwqcHV/9+RQFpyT6w/6NIZaO7+bA\nxaoue6Fa/Dsy4bxNS3aoXHnFiScNOmY4KZPNXzwolV8UJ2OIMUs9t7ErYngJ\nyLRZ\r\n=/vhu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB3yboBxuQefTdVLsfUy1LtDPERkPz8g5m0PSIcmN+0wAiBS3JNy1l2s4wrXQY7pA3Afjk3xdDtHfgrzAZ1qaYNRJw=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.0.0_1541922814801_0.45151703333191295"},"_hasShrinkwrap":false},"2.1.0":{"name":"react-recollect","version":"2.1.0","description":"**Featureless state management for React.**","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist"},"keywords":[],"author":"","license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"f6012cbb24966666430b64101b1d5d858a85b0ff","_id":"react-recollect@2.1.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-58iROBTdqItdVj97IVDPQfgEgFM/UypqeFWTxlGCqTGtuUnLbCPjQ0Q6LXPG1ls3aPGRrndb6lNeM+vzkFypmg==","shasum":"b711e2f6a9b2b9d4c74af6486e84fcc30faa9cc4","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.1.0.tgz","fileCount":33,"unpackedSize":66763,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb6oe4CRA9TVsSAnZWagAARh4QAIRuIGiF6oimkG43hzd1\nH504C9LXpQrC0pwfV6PKTED9LLzZykxtpwDRUvumZ8rbDZFFvM3RD38GYZJq\nhCZ1EAa05AZ2p71JC1SIsix5reIPZOJf6YPYksEOSCndWJONP5o52x9Y6vMn\nco4olxgPF/LCSynQLr6QE/skZUV8dJjG+2X2Itb1cwx6ouG82wRk9EJMAkbg\nR1DoN0+wAUZXocQ7gh9/HXpY1+fSGUhwa59/BAbDs+f3TWvdK0QC2T78FFQ7\nJ+CuTO/6mMYt6uuYchY5OrZNa8erH13bHIUZm1XzWo7oMXuORZCFlK/F4gCQ\nTRVRIWl1115Nkl/VBGGCJ1DLEbCnUC0zBJxS0j50eISwZXb78ecPKqmYvSm9\nD+m11GPU2uwvgL7Ke4iLdPcBJThgkTHcX4NyJRaA5PPZPXhs1HAeHI/UEgLT\ndz8HCFPHe9UclSwDxnpJ8dJtBwy0niLecNFrLB0fkEKO4xlsugKbtswzZlRP\n6ZHrE7uXry/f51Ec//g/wyx7TiNcfiN+LcYPRQw1nLo3DRil9ybzAma/mUPO\nJwjHhGWkRVIvWupYgER1EUDQQ9T9s6TuCdooduFlZL2HLleRB9hTq/E6d4+e\nHdUWWZmI7X+fzoPP2p3EdiChak9MXhHnUZZGw2Ead93+kaSScAyDlwzUVrmZ\n/3Qr\r\n=Oilx\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBukSzkkM8du28eRqVe/QRYB8Qr+SUIkuDpko1KkCp8PAiBe0GPRsulSOEOTHZYomWvt8Jn2uoIUw0F2r88THd26FQ=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.1.0_1542096824292_0.12610255577242735"},"_hasShrinkwrap":false},"2.1.1":{"name":"react-recollect","version":"2.1.1","description":"**Featureless state management for React.**","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"a0ada4bbcace56d02da22958a67a60a607201d49","_id":"react-recollect@2.1.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-LZtg8fvlQgK7fWgALGwBWj0dBuXyH1+PJdjLnj3k3u6cRzdTe7y/IIVxg9ot6o8orAMsyLbu8YcrOr0+vdvT7A==","shasum":"c93de3fc89cb0f1016856eb38fe9334359a5b925","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.1.1.tgz","fileCount":11,"unpackedSize":27908,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7oETCRA9TVsSAnZWagAAmUEQAJKantCKup7TEQcC1vYK\n349g7Xb6mSBs3aIx0WFFR7S8fiVCdR1ZBu0jw1QwxCexNvEFRsFBhPNkFCkM\nYeKxqgLR4/3Qg9+m2pRfnYX70Co8lJRpEAYf344rbOfcjGo0811WmH+Y58Wn\nu8qsttvabBtCshGSA2p09EnIcOW/BamaTkBp4Gpbuive8zrtNRfjOWgsoAGy\n6IXgbcza0f3h3uswgp59GbAl9Tw1Vmonk4fH8iqpp/MFS8Mb+6DC2ZqlFyxq\nZU9ejgjFmrBbWnR7oaxsLvvt1o0o65Xd3t+y7v3U7iZA/CYAg8mUZA2B/fGG\nFfppcDaoBt3c9+7D5b2qtQlNrAtohF8ORSu9e7dAo29DIUA2r/cf9OVp26IE\nHeB3YDqojL9Ov8u31TBjeNHkcfFLHzcYvuDCMLcHJn5Kg9Q88qd55C/Jzp31\nynyrL6Wq7ZZ7nukPbgtyqrqk/sT4Ol5+qJxsfZqYYSKFiS5jN1ERVH0cb7om\nzNMnWpCOzKNIHtuEj8z0lEgxZEff3jrQeaU65X0aMU8rXBDSNKX3EoS7zdTQ\nbLTmHi8ZmXNAbZRk/DzkWbTljy26o5yDdR7CUYQMRj2Vhil3IMwSYokMqG04\n8o+k1wLdGkEshVPNWvYjaYqWa+EE9lWaemTSFFHwliCuAEJsDYR4BzUVjn/P\nwsa4\r\n=W1l+\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHQWPhdy2tiMEGgIUczRqMSu3/ineHYONQyAhlg+apJJAiByjuR2NupZIrywngdRv+VmAPL3ipdqy9RPR5n2sioNLA=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.1.1_1542357266364_0.16651503918695076"},"_hasShrinkwrap":false},"2.1.2":{"name":"react-recollect","version":"2.1.2","description":"**Featureless state management for React.**","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js"},"gitHead":"59cc9d34127f5822a959446005d9d1219ada7aec","_id":"react-recollect@2.1.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-8KVFzOVhJwGW7kBpTJtzsxyWDIql5TrxgOoSSNuQq6GG7B8L/PJdGRJZ+k9xHO0osVBSy23uf3H6cFJQ9pcd+w==","shasum":"11e5a6e0b53feaf005361db792ebd82d7b45c28d","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.1.2.tgz","fileCount":20,"unpackedSize":51761,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb7oJcCRA9TVsSAnZWagAAvWMP+gLvn24FkdnQDBvhzoOi\nmgJN3+YMUwDMd2sWawK1OHYQwF2RuW0KHODP/dsLAGlorfgup1FY0VJFWKl7\n/GoKl2ss9m11fXpioVTKg6B7fkLaIFhEVkLqLnFzN/Q+hNjl/LLUqoNUqkyR\nEJLOc71xzErdFB73z9RliuG7aUH/JQp23+8E9T5sh3Rixs9ge40v6JpF2HlH\ndezNVhzisLBpDd5bs6YqM2J5FivTw/3nE4Z2F3bNHB4MME7ZLKtH9j7HcJIm\nQgwqyg2vyyouX8uPzN2ql1TYKFg84RoqWh4ommFCVfRzzFq6lbSKajeDp0zE\nLyMT7IT6/ZFPllgmt3ae2zwrTMqKzCkbm4I7UBOj01QNCNG1LkipF6J1ZJup\nJXGBuYGnEqbHQDDc2WXERf4ih8e9T8+5tbMOq0LMJC/tW0NIS9YuGPZ7rADj\nOXCtS0GRWmrwqL/hl/upNVmb+JbGBSFf8rGo/PIAw2yscGBAb+3RzkHG1PRg\nXoPkQiiJZ8mV//ejqYs9qMa1wRfahm9bpn0aztuJNoH8UtApGFLb+IEUPCym\nFXECrWmnr+vffq9PNWVdIM91FxGuVIVlIFHKvBXFkxfxTOe2fAgp5cHPgY8Z\njCjppVyOEm9aSHTQ1fSWj8DQBAEy1F9AqL+7OPmKxftGBVuCKpG7GfYuS8Vj\nE3iU\r\n=zWCy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDug1VGZfPRHKpU8LxE4WoCu0BbliDi/ochOv7tbOhsZAiEAs5KeFvcNw8q9C7nDwmavZS57wpqgvnagM53DWEOdUTc="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.1.2_1542357595869_0.7382279338491677"},"_hasShrinkwrap":false},"2.2.0":{"name":"react-recollect","version":"2.2.0","description":"**Featureless state management for React.**","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist","start":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"ISC","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","gitHead":"1e8d0b86c831d09b0c1e243dcc980b3331432d72","_id":"react-recollect@2.2.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-ZeOpPGcbusr/ON9Uxqd+77Td2jgmPb2Np7R12djMt/mzsK8w38RGwYfCQ12hzSFdiyOh9oiJuXbi9k1LgC0K3A==","shasum":"30c247e47e9513e412371b32225ac71710fd6543","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.2.0.tgz","fileCount":20,"unpackedSize":45937,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb78KzCRA9TVsSAnZWagAAGM8P/2h1YD0Bi525O4Ce6Fwp\n1CPGEQ6+5WWPHNX0Vj5StPHpTzrnt5/fvEi/Y2q9ViK0opV+XCJKG1DfvkO7\neSMFZrRUmMq1zi2UHje6K2w2KiXyzpF4lGwWaSexHk322P3rP5u583kxJP9m\ngSw8BehKtu2GmLez8EVjiI3PvEu5Eg2iACZRlh4mVFtnxg7INsU88ZJ44KjG\noUp/MzxbMElR5cWmr1n8S21ClBqvxK9jZ16wf6Gqm4nC8a3ZECOQLFOjq9RX\noQSd4aM9dkZCmzSsmrtxZVoRBPRgCtGIA4nJwQ7cWC/IR04Qo6P+Tuk4GwR4\n0nL0Gu4JjbJR8kSAgJyLyYlBcgmhD4Fruo11HzeelOwVxDy19vvENqmPcIDm\nbJEHnUyz0HmUfyE9TKVr7T9dqu+9LA5j1L8O/6zeqjFGBfkosGoR2tOdHbpe\ny5olUkj0Gj+xEmxRaN5zmebPslFAL6wtaEh5f69xKf6ijkraP8vdIAYfPodA\nsZceJ1vyD9PXPCQuee0WSF/iPsFkiRAEvpWizRs1SXBu14zRiA9n9zI2WyPl\nxz5gZDwsc40C7N2mMzAu8BkPw0ROGGF7LtKMyzM2GLx45MfK84DCnu0Gx0zF\n5p5d92gW3evUzlECtESshpT+ynpf2Qq7M6SG4McxarEJ+KnpZ48XpFvwPgcH\nVigo\r\n=WyDN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIDpu0s/nGYFrnqtSkSXx7NoVQLZ6K/p7BzSSnM5wwZkOAiBuybKuVRrOGB/yQrB3kPCgd2P/nOZ9E4bIc5wTyS0v7Q=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.2.0_1542439602634_0.7300225588443026"},"_hasShrinkwrap":false},"2.2.1":{"name":"react-recollect","version":"2.2.1","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist","start":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","gitHead":"d8ca9b1000fe30eed64bc7fc395488af8aaeb0e3","_id":"react-recollect@2.2.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-lLqzcj5rWLrkOLZJlpZxYuMF+2gm/fAiaZhFXQzIZT857DR/t8S+Us3S3/9GBpELi26RnlY3BiJje1wtkioNVA==","shasum":"a24c85d95eed51b221769da74e32be10f2b821cd","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.2.1.tgz","fileCount":22,"unpackedSize":49006,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8S1jCRA9TVsSAnZWagAAfAAP/RUzv/pncmKo5ftl07Js\nYKcIcRfvI//N2mu8wVfIkitXzr+z7uzRBZpSyilWFKFNp0WGXJxC8s3lD2Qk\noIpdViTPeOl0xXy1dPRbq0jpuSk0vDRDEEn2cp2EPnJNBl+qniu4IrZPLVcS\n2/+EFRd/XFl+mDO+bVISINZHPV5lU/TH7Mc27mpflq/8BMpLzYTzQkLiInkW\nkVifzCd3DlPVfG7Y185HtOiRWVJhRkIeBCTvzcsw/s/8gZkqI/Bt8P0422u0\nUagQpOZMCHqqvKDdlM16SXkX1uYSLT2W8UgIDPKEkeLDXr3372936VyAMRFi\nDXSoXGfrCZezM6FH0iUY4cscvmS4OhNX9+UYC26baBiVp5MAYB3fJd8vcR8t\nrdZeBVt7UDOWw7Dkm0WXblsinEiTQnkjx9mXkGmW69u8OYb4pgSHKHqmMXtj\ndzULCUjeOUw6INRNp4iXWBgm2P1Gy+XHUJefkgRe+cZw2QEcdMVj2+D6enI+\nNPPBNi7yQSwBph+cwzvCo3UbMZUvUpxecW4CCG5cHaFxfD5gXSjUCxvFsIZH\nfuJvn6APLqJ1rgZNUr60ZLRpWDQa0nM2lyE3QIub8hfnqSNBfGD7PQqJrzhl\n5T3ohGYgDYF6HUpJY4SG1YyMHmNgcB7WxUpvUo+kr4hSVY6AXJMUAdOo478d\nX2Hx\r\n=8ToC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCjywPuk9IWCgHpN9g40Nq58ITw3dyJ5MYxOMMJcLHs4AIhAIJ39upp7VuV5vfO7F6Q5YQPbwtoJk9feRZDouv2i5xg"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.2.1_1542532450750_0.3686012284803415"},"_hasShrinkwrap":false},"2.2.2":{"name":"react-recollect","version":"2.2.2","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist","start":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","gitHead":"64d04788712e61019571586eee9696d18b2f6f02","_id":"react-recollect@2.2.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-mq/tlc4nd1S2Yyi+c3AeR7YZR4WIe0mSFo9IqcE6L2KjShvH0E97QGv+hlMwoSQMVC+cq54eHYZGjFCcarHRcQ==","shasum":"3fc17ba6799b295bff92d20e617143688da9f52c","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.2.2.tgz","fileCount":22,"unpackedSize":49128,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb8S8pCRA9TVsSAnZWagAAyKcP/0X/rwSg09KrqDVsgO/3\niHrXdR3k/frkBuOgl8621c4ARJrHGXVAtf1Q/Ea1QOKrylEFCmXn9RG0n8LH\n6w9MkEDFbCbSM2tZF2V7teHJiuVF3ztqVrIgR65ze1q4dzk+YNkj0UZD1m9f\nCI5GuADtxrdN5RjVS2XBqUQeOgtA6mtZzESq9JdCTZPFT9jVFLID1Kpyfjsl\nm7c2U9zC+uSuq13JLBcrRUDhSPQYkdyDMzPrxbptP2JrTbKlpInqbdGDUAQV\nQ/dxJ/YP6deJCq8g+ExZKhuz0HKW3UbfQOklFw/csSK9iwcH3CFMWDKuQQMr\nLk1ZCkmp8R/4rNVA9swo7wJIJ+xs5fjfuman1L/gIx2TawNdQiR/JHg3lX91\nIV3QDwbAXA3lfxjiPB94pxuNwEjpaQUpFPXa2nzBQsqePEUZQ49CPB2UyEzj\nf4zJuIyZw27EFelhG8lEgJjYn3QPf+htYDJ/RO1Q1ZMvJ1RjhNL9gbc86GUG\nmLp5LVrSH1swO/KtowTHJdVGTN9CRHiX0lm3/dXTH9IXZbVyRCUMMkrnbM1/\nmJtVC22gaeSHA9m3rv4VQhzmg62kToccY2mzKQdjASKZhObkG0a0jyupPi0T\n6CEbUhP2nDG6mtnlMpp95TttOMGWPVSJZAS22C/Y8UQxta0+5yhgu3LTaecI\nor5e\r\n=1pTe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCnf2AWIjC95JNtR3pB2Z2LIW+PACTI8lnNWR+1yspRUQIgVWE1GeNCudd0ThymtY9kWhBLZNDCuGhUGSz8iTyP0lw="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.2.2_1542532904644_0.34643077150117607"},"_hasShrinkwrap":false},"2.3.0":{"name":"react-recollect","version":"2.3.0","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"jest && npm run build","test":"jest --watch","build":"babel src -d dist","start":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","dependencies":{"hoist-non-react-statics":"^3.1.0"},"gitHead":"4e9ce026a7ab97c06358babcecdca319ac6b8444","_id":"react-recollect@2.3.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-ZB4HdmXOcYYhQ/6cO7zMsDoVkadbG0zuY6e0wml7KuUyVlal4LAAPqlO9Ydl/U4ar0IGOYeEXNeA8pLYhCV0Tg==","shasum":"50594a58322f0ec69c212159e24ee344ca6c24ba","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.3.0.tgz","fileCount":23,"unpackedSize":50954,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+HCxCRA9TVsSAnZWagAAPEQQAJNDAeS/Ax3ryFo/fVH9\nTBtlMAWlzI0unV7d/WdjGd3BneZjKiDH1hnz0u4GEBcILK86u2Zq9QoGFUat\ngGT0fljN1+jbv1VwXAGCsIa/t/xiOdoGGRgtRRDeIVBKWO9wZXNkK68voHKd\n6p65oSmimAa4uCcTPs3RZN9C+CpzoUrnCFdv1YsYQZLWNdY3GHlW3ZE/2HdZ\nXpeD9yLV+xXFtbHuMqSyaMkKUpIMKY6vjmB30DhEXs8HB1ed5AxgkhkalV5T\nK3rXSu2T1R2nL/Ida4pPD6CTAXADXgSjBfwhR9lFVfYR87eTFZNEpF4wb5V2\nQtTbmaz5FIIIrcJKHITc7QF6rd1b3UEHEqQXpYBnt1zDn4NX++JWS2D93M+q\nXDdWVHfx5RHLShn+2nXpuTnktpGe5jBNExD2S3rWYjFb9MPdwH+/pMyAbSc0\n/X9DlKQqiWPpOdnYYZAKj+SmNAMhDTN9o1fPDwnXudANFdQuUBQQ8HhesnXF\n7vjAn7gWtPpfsdFMmRUK/zh+OOl/+v+mbZP5M2B3pg3FK5CnLz/dHHPxnv5f\n6FHI9pUYjQDs+OP/rruD9aaxvd0gFMb6YAATVLOBP6OWB25tQOhtZxJumUox\nupJCJC3X8F7XDQYF80XzjKwVaB18znkKRg/dRYope9hP37rn9nC7iMStioZK\nicKP\r\n=yzgy\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBvbf4W/17/jIYi8xgXqvoCz56KucvkGqMq4IHZ9TEL+AiEA9rre0ZpsoIY1/jS2eTxyrYk5uFcMjHLUVWggskfObwo="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.3.0_1543008432943_0.6536593059537008"},"_hasShrinkwrap":false},"2.4.0":{"name":"react-recollect","version":"2.4.0","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"babel src -d dist","build:watch":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","dependencies":{"hoist-non-react-statics":"^3.1.0"},"gitHead":"61f95ffd51bf2b8e561d910b2d30764274ee1f21","_id":"react-recollect@2.4.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-aptQNn6mldpuMvZ/PmgvPJENyBzVZr/anAPEj8JNA7483XaH7m5cqPJYMSjddoGqqEJvjQBubAetvQTCX6bL/g==","shasum":"4546c37dfdc60e7ee27582107f8c56f871d96054","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-2.4.0.tgz","fileCount":23,"unpackedSize":56233,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+SOcCRA9TVsSAnZWagAAhycP/259V8qkiyF0OLf8fIsT\nucWcMopYQdCDMcZXm5gUW8SWzLZNRcw3Wjx7ewPPK1yvcVklnS6+1uZXlz8C\nRDXuNgp/csH0UdqaIWzY1K16EL75JRH8XnvHZK/j5iLt9oNfOrrnWPBadskQ\nezqbzmd+OqMpAFPdGffHEthKj4BKxPP7WCGXvYjfbMXPrr5h6ZXAQfetVsky\nlNqu5liBHfRmDYBixSc2fyP20QKlhU3b5FJ+jSfOAAr356i046yWl3sZ1fYW\nGKLILIGSw6KPEvplKP+y/+zmUGcJ0sY26zrZYSmCle5/M5XYfVZy7oowWQrs\nBjdNGHFyv+PK2z4Il/8YfNNprJp9r4XtSe7mO/OW0O3DHQxvOk18TSXEvkKu\n39vwOaEx5dgTU2zENa1J/DRK3ezrnOvIFlIIw93GHHLCuc+yQu4wjPa3K7c4\nv6/AX/XqYz0/XzQzmy0fT+SGjqcGss/CNZOeLtmNBzNSloX9aFb6zCxOthhw\nPKxYL8s8BJHuzzvXpqhMlwoAV8Id7aGY+PuH8oYiAhmCVHT0lpuc5Ob7JGno\ntnPePH6lDy49yZqd9bgF8H+wwER6MjdBTzu+iiCKof+3Q7AkdNyfUYLci4xg\nAYgGEGtjDtkb38Ot9Bff6EhSjlHk41HBEUPqQxXHjYF41+8h9kMEaLPF4gfv\nPpPy\r\n=j1er\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIHF/CB1pO8vFi5UFODUtWz0chZGQw8Z1japVbJfshanHAiA5p+rZewYC1CwKgFoyUkQsNiw0Dnh8ppFXT6nzpSc3WA=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_2.4.0_1543054235667_0.2887734033991165"},"_hasShrinkwrap":false},"3.0.0":{"name":"react-recollect","version":"3.0.0","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"babel src -d dist","build:watch":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","dependencies":{"hoist-non-react-statics":"^3.1.0"},"gitHead":"d0aadfc4aaa2a077f9061e8b7f8dd8f657f38f51","_id":"react-recollect@3.0.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-F/Z/rSzsof0kRIEHcdt8x+v78Le7cwXxJCyupJl3ntXfhlwvLV7mog/9ifBRF1aeWN431zQDYLZSjsSKj5Mvrg==","shasum":"63a1c523c22e08952d4a5b898c221c52c8033db4","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.0.0.tgz","fileCount":23,"unpackedSize":59618,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+icOCRA9TVsSAnZWagAAr7YQAJxr2DstK6/AsjSHnTQ1\n8yEdGINfVznY1a47EJrrX204iXshdbafek10fkrxrnhe7zO/sHBRtQCisiog\nIQJIG/+FCNZt7NAHuM7Wa6JHqzYrvRpTOvFq3McShl3HQ6w9GnSQUmHdQ5bC\nDp65FFPkh6U8zljeezaAiXiWvj0+JD+vBhGhJc5xsCS4KjrQWc+8m5KnwDkh\n4SurtOL0wj811x350dkH/fiGP24QOqyhPlqRV4IKFoy/NTDPFBZ8yAy5MhbF\no3BevNxQgolf13FdzdTyIYm69HyEDVf6j+0MwNpdZQX6Fxn+iGGqpsWwaFcD\nYn6bYbeNALIR4fNBqs2Hc0YTZQGJi9eJ4QD0zTPSzh3INqb/pl6mRf4W35VE\nnxZOFPFJHj1ljhVax1ZUwGpL1ByM0IlpOLejD9dmHYiDfPdtDchYZ51KYsyf\nzec6ULh2WLTNT05J2hAoxPKG7FXZAGEYycXUgZOBBdZ8eCGLCeaBrtCvvk4g\naK6XslofMplkgSNMnyFhNcslrAL3ODiYQPRQIWKCTXiQd1NXhjDaRtx3ThvQ\nYNjWqWOwfrEF6QybzobnmfGrfiz/rstla+wljXAY7y2BYKwLD9/AhNkLVM/F\nlifpawahUNZ5O6EwsTumhqQQimObm9kBanNv0ZrVXAFrGiZ/mhnu5lW+OuCE\nzpwS\r\n=CpWp\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIG+VJhlhLiy2dgPG1Z/tBCQIKGaAPLyaIpV0JvffP0feAiBZyjmquxSF2qgoYfuLSZFa5N/eFl8J6Neg1Dlnjffmsw=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.0.0_1543120653527_0.5524111155538078"},"_hasShrinkwrap":false},"3.0.1":{"name":"react-recollect","version":"3.0.1","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"babel src -d dist","build:watch":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","dependencies":{"hoist-non-react-statics":"^3.1.0"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"259f6b72b3631721da0042897a4ab6ba22cab735","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.0.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-zLvd2D5BQxq80gBoA+GovxcT/WfBdT/UIMSofhkO3gzrc1mXg+jLsz2w0VfrzsDDc3VP4EW+AlPfCzUDHu7fKg==","shasum":"02f9f7cb02dc192827bd515dbd6156da4dbd150d","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.0.1.tgz","fileCount":23,"unpackedSize":62082,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+jVGCRA9TVsSAnZWagAAPr4P/0jh2pm+EviUsfAh6wi0\nc+s7w+Jei8+HqF2adr9UaEWATKSeopSF0LDMhB/VmERmxgyAKMZ2upCBwxcb\nd+mHbIsGqxIcQlyO16bXs6tn548T4TaUnDClWmtELD5Lxv2vr+FFk4yQfjRo\nDbAOjreISBQ/4vJi5WzZX8n998stRuahFtczwQQoSQZTnDpdESP4rXejsY5h\nmXTicAVYfB0VStwUB+KTuiw3HLobDt0KhsmoS+Oh4QP3Z6JMrP2cB0C1k1/+\nThnsVhtv5aSVqYG2BqIUwAO5YTE8f78XI6qM2Jf4HqJgsJqGhiQCdP6KPhhT\ncccHichy+lDPfq8s4D8KsAgBmJGyubyrI0Q6jhabsvRh262CZhUH26ZG0y+Y\nCErEO+PjZD6cszgEONn3ebDgf21Fr+wdxnxl7wWMCRaWvDG4RCZu0vtoYeTr\nA0AM8GEV554ad+MuLmb4FTwBTAnzQsdIGTjfTbea+aLT8TyvRZfOlFqsT+Ft\nH4Ak7qppLJB4dukaTapnhXI8IOu3pcG5kkrasqIhb9hrYjhZIFxxicQymTxv\n1F6lSbxT6ItdfnkNLLHuoPuVVq8u90AI2RyYbt7P6MRQnThlCggQuI3Mbbo0\n0JHHjc3dL0kLJkkmcAa3JU3o9ja/4NKVBAbN+bKL7GZMo1Z5jS7xZBEKvIrL\nR6+h\r\n=sDh4\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQD8Ie4UN+RyONFafEmEshM2dJv9lFu+cP2Oa5ofJcRGAgIgIqhrtYR7i/S7eJFDGCB4cDsODcOWJkDGZQjJd8QXBSA="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.0.1_1543124293647_0.985157932025966"},"_hasShrinkwrap":false},"3.0.2":{"name":"react-recollect","version":"3.0.2","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"babel src -d dist","build:watch":"babel src -d dist --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","dependencies":{"hoist-non-react-statics":"^3.1.0"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"606ef25919b771244723a23ddea041bced308ae0","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.0.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-vWszNokzFylSMIH/Ic6a5c+yiNjNLMiekpbTZZkufzaro+W6lGsVucJ62fC9fH8WcKABQisOsDSxk0BtXLIT8Q==","shasum":"9620dfc5b50920f6c7a96537f3b6bb811f52e865","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.0.2.tgz","fileCount":23,"unpackedSize":72391,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJb+kA+CRA9TVsSAnZWagAABy8P/jVRygtpwGmguO2gg1dp\nEpoY9rK0cZlSjA9M4gftWYoIwqWCHMtKdI5jchj/prRSD4RjSVDcSoYzyPk1\nUcwZfFyF1phsWZWZXgOVg4jRpqX6TJ+HSd0SkNpyLYv20S2l0S+3PBmTt7Ag\nrj+0dqoUZ4RPERDjT7pS5qPtbg+lBRieYix3rw44lG/NGdLYj4b6Qjc8mKW0\nPCEhfFxEABjpo7FKiod9M67Pv+hNg8Oo4ma5lAS285WHM+CfY4Li1t5pvvoi\na7208Vh+C1AN71xEAnSVoiy4ZRZzlALwu8I4NxAsELuTdYmmWZjCRHy2XMOR\nJUCs+6Y5qcdEjC6xhm2rSaZ77M/u3B3AXwcLGWq3gcalpx9wZ9fck1CuVReu\nPi50L+mjbMQ6rbBO9ha2/X6/fuSpt9vK7VDPyekm3k1kZzGYl/InyD3AX1h6\nVKKQe58JwaWPpKxu2NdYluaIQwlR5I1KCCPlU7vBSM1Pklu0ZYNWjOIWOXnw\nkmjF4zIiHspMeJ4pjXCaDu2+yc4D+zDaHdKz6T6Z7MyuEzcq1yQueoEYg31t\nmyaHcTyJGIJGjc8klxhI1TxqhOLVQwMWOMc1V//RqeLGGY8KN6/i4a/eOKoT\ngJex9EM1L0S/CAf6I67ejZPcfPb1U0UbrVip2mCoy88iD0JJsgh+Uu0/AuFe\nqC6U\r\n=6iBC\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBiHBN4w+O1JMVTOBPSYUIRJdMkLduuFq5w2EPWItTG4AiEAtLXRLkNHkb7SdK2xdeiqMDMxDQUvkVIGMAA9QJRW7+o="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.0.2_1543127102138_0.5401416672078418"},"_hasShrinkwrap":false},"3.0.3":{"name":"react-recollect","version":"3.0.3","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"webpack","build:watch":"webpack --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","dependencies":{"hoist-non-react-statics":"^3.1.0"},"peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","babel-loader":"^8.0.4","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3","webpack":"^4.26.1","webpack-cli":"^3.1.2"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"88d60f94c0b2c3b81936d1db8491a514cb8efc72","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.0.3","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-b/gp+PkzQpl0fSOezbsia5D3FE3Qheu8Z33bPURIR03PF+xAolT0HcgWVTRjkaBO0h4EAF0S/99h3ZK1szERmw==","shasum":"177692c642331bb6b0b3e8ac02bca2be277ba946","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.0.3.tgz","fileCount":14,"unpackedSize":64025,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAjRqCRA9TVsSAnZWagAAaQwP/3lBkB/gOPqpp3HlpYpr\nZ6W+M+JT9x9jmimSPxAJFAMIoSrfeoC8vOtPo7IIpoHI7fF+JTY1M+Qvdv/4\nJP2sX4VUopZR2KZHVERU/BDd8zj2X5ol99EwZzWXFSygPeP/CteGNcrW9Bb+\n0xyG8Mbn//l4E6RTe8xjknA68J11dGRto4E8Mc4a8upjPJeU0iHqeGKbxM3U\nO+JIO2naqRPMrq5KU9fGLZy3ZWOwMJbCxl4SQqjlbCTu4U4pLDILZvvyVWhM\n2UdJmddsbrSnjYru1vyzqen4UQWBm7lr7EeKOQvEyV5epQnwsIK8TxMMbQvt\nz/1sLgm4PiV5eucOIiq0kTNgIYrfJlL/T1w4eYCvM487Xepp43M40DtkFF4Z\nD8R6Ya9OMiEOFRiAK3hQnvA5WhWP6csK8Ba0Ql8j+gD8EXAeYc/Ap2x3rrzb\nzjj+RcWDJrUgRhRq4CaEn2vUTbEVixyBhq7gRrT22hqFZ7vk+Dy64j8GGEqU\n52ZfaNQJ9hyW1Gv3nG/mn/bKaX2KthTbQ4jlBTA5K+N/4WeweGnOzmWDTuei\nKd9jCz356AtavSX+ATTGeiNGwDbdg3jGIvbzr7wkm9KQSvc+mb9DVj/mowu5\nGH9xuDYNMV0fmwQMP7UQ+Wj4uW0/G3KNNRfrfUtqvTqvP2kq7wbHPt11zfWX\nOd5g\r\n=I1jF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCV3RogCW7H736EVkLZPZA9uCa9WjRihl1ZfHs3GLEyggIgf3AK2gf27M2y9PhGoLTW7vnF7PFxP1gH+oGmcAjc77I="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.0.3_1543648361330_0.5473968337581518"},"_hasShrinkwrap":false},"3.1.0":{"name":"react-recollect","version":"3.1.0","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"webpack","build:watch":"webpack --watch"},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","dependencies":{"hoist-non-react-statics":"^3.1.0"},"peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","babel-loader":"^8.0.4","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3","webpack":"^4.26.1","webpack-cli":"^3.1.2"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"5b83c01bfe8fbc923076b953dc039a1cdf04ffc9","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.1.0","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-+MJJDQqyiWgzBJT9smMMOmnF/iKXu2H99PXrAnZaDeSF/kp21lLJCyPGr4XAsKr7uTZ2k4M0TwVkqkwPN8Exyw==","shasum":"be8f600d655f48cede74aea2bc50fceab6b0b939","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.1.0.tgz","fileCount":14,"unpackedSize":64462,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAwX2CRA9TVsSAnZWagAAQm8P/jXYY8zYZ2wKUr7AQkIG\nIvinut40msjUrRt6ou77apFAnMXJCHoRvtldsFVxV1f/+NjihVDybKqiWIUS\n5zH67sKUBjWGv/F9zmYnM536oCQ3f0yz4zFYj10lzdvufVvOm8oAAGVuMhG8\nhhzSWvgAStwRH5cz8hHOilynfwfYauB6/v6vFuA8emV6/l2kpUbh9Ed00E8t\nqoZ9eI0VQFeRedkYbtOv0/C4vSV3mh2g3WxC5HQzci9u4HcuSk75RA0I2p12\nf7tmBiFwqU/jFlxKgqUy3GQpzr/E9mLPmdi5AzaxF8KCD5WgE7WTz94rAyJu\nzULAsbA+tLf9SieICbjo5VXINan0TB+4YhTEwYVljh64k/p0vDFZVkqLxH5h\nb4EgpQLEpl4gHu6vjJluytfulYtfseA/8ubyqBwTS7QFCnhTlT2oFyJvEs48\nYXpUda5snWx0XTDo0+SV+diPgGN3ladRewwFcGl106cArBi7I/9d630+d6Yw\ngxfhOX/8VdfySE/CrIAhiUqcZREb+Uql8EQEbekCxh8mk9ZW7uswRhxlT3NH\n+HsNkAG6WGcUA3+sd5c3PdmKTZl+w6dEu1ALh/Ew0Ax1igsHOl0n3ITmqI3o\nTpZ3q8c9ePgcU1U/0vb1kr35sL3+Knh8ugwvgOWWHx5lKq05XxX5VJ/8XkOg\n/C2l\r\n=R+Zk\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIC2F3R6YIunYTSUrQX8IUuKRpj3IO96mWx7dL7AQwT0DAiEAjcMUMWLtvV+88FZv5rHPwXWFiOViKRVQhBYr5J7o/W8="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.1.0_1543702005811_0.6810086905579587"},"_hasShrinkwrap":false},"3.1.1":{"name":"react-recollect","version":"3.1.1","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"webpack","build:watch":"webpack --watch --mode=\"development\""},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","dependencies":{"hoist-non-react-statics":"^3.1.0"},"peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","babel-loader":"^8.0.4","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3","webpack":"^4.26.1","webpack-cli":"^3.1.2"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"1f2bb133cccee390ec33e115693b8793bef433dc","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.1.1","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-/XXmcP0t7lJklgOy1sORGugfAd9Ru8coBQIX3hq7IpTHXa2JnaerPmIj0LiEdhGUP/cjEKU9teZcf99UV5/KHA==","shasum":"547a0cf188a02266e729ef40537fac33251d10c2","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.1.1.tgz","fileCount":14,"unpackedSize":64731,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcAzIoCRA9TVsSAnZWagAARhAQAI37v1Mye1RXez4qobzp\nULf0k4+w2aQHIFRmw8LmeWJnEPwY1jVlbDTsz5CgUL1JiOcax5IoKK4sk6OD\nI4g1I8/MTGzEZDrNW9KV2Yl58obkxSTGiFMyNJnPHUCwR2shYSEDpRi7sJTA\ngV9MdMvrOKZWRh9nfEcL4E2BMbxzspPiJ5fE6QkxzRPhSNqNPwtgq26LjKV+\nqnfu7X4uKut3y1TIzLifvs9KHYELsSzMI1We/VEfPXSWkW+CpH0SKdkk/bQD\nbCz3mPI2wGgir+KawZMIUfZo4t2RWdcMjja6XB+ah38hofGO47TJ9VwLd63f\nDJm5qjV/xQbjHFy6ZKHZHJxyNc0jQ6qrUEPJSm7Ej/9/tEgo8BOKDPRCl9cq\nbqlYtrcEBOiwBVFmTiUxttpai/iA4YPpHdrQgnkWrbvLmOwliwXZwLIHfNId\nqT9MuoGwUthWD8EcpedL7tgqtdzZQmKh3d4r+jaiQUEurUvER5Ya4GkLX5z+\nd1VvRfD2ejl3bnlQKDPjx+R6waG+/SZBOUBw1w/WqdfMU6sxdQoc23p5F0DB\nLLLUOlaKSnU+bdeJQTAydI6E/EqR+xhKnonlhA9wIfs4bDlWJacAUaWoHELo\nC99GHJ03X7JFj6OVkuPuTRtqgXnDgymhIGeJLqB9ZvNbVXgjHMFXgsb18vd1\n2B5t\r\n=DNm5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDBKHO/+tKQ638TLPsAW/5grtQFRAC4fj2Ku2oplN3rCwIhANiA+0vn+qaL7CU1/LSo2q7OEpdhbDAx9NSIbBevc4M9"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.1.1_1543713320182_0.68316743906165"},"_hasShrinkwrap":false},"3.1.2":{"name":"react-recollect","version":"3.1.2","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"webpack","build:watch":"webpack --watch --mode=\"development\""},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","dependencies":{"hoist-non-react-statics":"^3.1.0"},"peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/cli":"^7.1.2","@babel/core":"^7.1.2","@babel/preset-env":"^7.1.0","@babel/preset-react":"^7.0.0","babel-core":"^7.0.0-bridge.0","babel-jest":"^23.6.0","babel-loader":"^8.0.4","enzyme":"^3.7.0","jest":"^23.6.0","jest-dom":"^2.1.1","react":"^16.6.0","react-dom":"^16.6.0","react-testing-library":"^5.2.3","webpack":"^4.26.1","webpack-cli":"^3.1.2"},"jest":{"setupTestFrameworkScriptFile":"./jestSetup.js","clearMocks":true},"types":"./index.d.ts","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"95013dfdb114a7fc86c018431b62a41a38358a8a","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.1.2","_npmVersion":"6.4.1","_nodeVersion":"10.12.0","_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"dist":{"integrity":"sha512-rXn8m/iAWiEKHH8YYo3ameURbr+hs2wOkIRpdkwI3c+pemxEseykhfRP9/nl8LPmlnqea5iG5kHqOwCgDkWPgw==","shasum":"b0244ccf244a3396a4da3c318598cd97985ecb07","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.1.2.tgz","fileCount":14,"unpackedSize":64814,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcTnezCRA9TVsSAnZWagAAed8P/jwAVzbn9uOL1UaauUmC\n4Wz2xUebInZN2dCuD7HsLUK/SXTH1Ig7k6zpZyBnPIru/PgSw2/yfg0/P++4\njyxd5/c/fY5ePF4mVPOtjWobSc8eSLu1kBfVUlZV9tX7Ogt/Gh1AY6Gs3Jy3\n8PS7h49xM8dfVXQpYonEi9QNeOd0zODiBFZU0MJpxyKGrMjAjr2BwRpM88bY\n2T98vUTIgvQYCbmdPJscwyENWuj8glcVKzXG47raHYflIYSO00nGozlojvpN\nIDV0KX/38uoGRWYzvFcUOolEWHmolR+S7frgOkLJFsAhITs3ec05YmxFIXE0\nkyHAMLp9c3R+fYcg76xPAs8eg8lRLQQ4S47lVP+HCk3q6Dd8liKBlP8cHUAm\nzO/RXmBzSsiNodYdzJd/EAoF/BtgSN7zIO2qV0f0Kz7pYcveejO5n+uyIH/j\npU1qH0G2uqaon1/X7LEMLdI4aYRsnRqKZuv/VOwR2ljOdIkUoMCYudHlCx5f\nvin7EZR7h1UgClZ2+FFoMnIRmMcEpyGnmZ40RfmVnWbSdjllNBVTylB4OPBa\nVkygAflpCIIP7GRp1TH1SbfM4mm1gO/MEiKNYReG8rAHsxPXZR5bUMc31aeC\n6rfuN3ScibmVamZwrxkWdeo1v4oVB+sQjdBB/llTgZkNugNXYa2CKAI5Cyjf\nFMX6\r\n=sQ0A\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCksSR9mXfxY+Nzj5ehzqqrYcnLnXdPp0lP3y5ftrFl4QIgMHC6Z5lD4gzgqu3Kjbs/R0oFYjDJ/lUkztsBmzsnLPo="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.1.2_1548646322084_0.3892994042919029"},"_hasShrinkwrap":false},"3.1.3":{"name":"react-recollect","version":"3.1.3","description":"Simple state management for react","main":"dist/index.js","scripts":{"preversion":"npm run build && jest","test":"jest --watch","build":"webpack","build:watch":"webpack --watch --mode=\"development\""},"keywords":["flux","react","redux","state","state management"],"author":{"name":"David Gilbertson"},"license":"MIT","dependencies":{"hoist-non-react-statics":"^3.3.2"},"peerDependencies":{"react":">=15.3"},"devDependencies":{"@babel/core":"^7.8.4","@babel/preset-env":"^7.8.4","@babel/preset-react":"^7.8.3","babel-jest":"^25.1.0","babel-loader":"^8.0.6","enzyme":"^3.11.0","jest":"^25.1.0","jest-dom":"^2.1.1","react":"^16.12.0","react-dom":"^16.12.0","react-testing-library":"^5.9.0","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"jest":{"setupFilesAfterEnv":["./jestSetup.js"],"clearMocks":true},"types":"./index.d.ts","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"gitHead":"383533abe01fdc291332767fa79631e17c5af0de","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@3.1.3","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-qv8RGhEibAhEaJB4UlpEQ3KNIJPQRry9DsmsvJfow7EGclpLhyjH2ugiHl7U2Gkrr5NiA39buoRSoQWRNEoUQA==","shasum":"a70cabc314f1d0a92280fc053b99ffcb455b892d","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-3.1.3.tgz","fileCount":14,"unpackedSize":65534,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeRKVICRA9TVsSAnZWagAABIkQAIc4OHVs4/eG9jC6t3zg\nMUuFsnzaa64PAch5WUTAclzYQdGNb2nr5pg17nVYfoGhYPGfRgvzdbZkOYSi\n1vrs/LuWCC44mXN7JsEcuP4eS+sezO+r9pXx5Cu6gjmwRSoG0v/CEzk54N2p\ntueGYVwsrg/584QMoMdoqPdCHYzWWmUZhAn7CcjQtkn6HhAN0/5dtRFVHxHh\nhWG3DnH/FJAicV3Qw+Nb9aOTzByols4aV0rBfO0dQtgXJMAS287gpqxKl48u\npzykQred4EeOqZbhHzgZkJVey2YSNvdlkPmgQFdTTDtzNYBhieQHZv+qAlqN\nree0Gv+hNnjT4F+lwwEAU5lLsiWHBXSRKid6FV7Jrco3mcfYZgKDGdE6iJpy\nROw5UxOlC+L5iIZ9eDQq6xXcUutCdKvS7mKTdZq+assKlyiFjWkc8JQ83dZW\npLL3fCiKFG7JBORLjnVhjFXJ7FmC+zHVMrE4pmN3J1WWUHZN7TD0HvRU/Lv5\njMj05E/eu97weUTy9biYlWIkE/BeqTT/oA0OPKgWtxt9tj/J2i+n+ZkNlk5j\nISMts+W3tlA9R8aObJOaPo4oAR80gt9GVT7rcukX5Fn7bmjqnf3YrJOiIYg4\npwLAExRsfomMofPq9uLbU8JYVgSXBgCOpS02Gi7Dm4uhJ9TEuWLXMHyo9yNO\n5tN3\r\n=uj8/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCwgvRCzuDHb6IvotnPk933TGkXkRdarsUISXIxJ6ODWwIhAOgPdbA4C6F2X+uCYN8cYulXgUOepTtqeSpbpFGYhc6P"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_3.1.3_1581557064240_0.08003802712002783"},"_hasShrinkwrap":false},"4.0.0-beta.0":{"name":"react-recollect","version":"4.0.0-beta.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","lint":"eslint src tests --fix --cache --ext js,ts,tsx","test":"jest --watchAll","test:ci":"jest --ci","preversion":"npm run build && jest"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"94304bb3f27321e7498bd49aceca27fd56e1a392","readme":"![Node.js CI](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\nRecollect is a state management library that aims to solve two problems with the\ntraditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed\n\nRecollect solves these problems like so:\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object. No\n   need to worry about accidentally mutating the store; with Recollect that's\n   impossible.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that read\n   that specific property when last rendered is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\nThere is no support for any version of IE, Opera mini, or Android browser 4.4\n(because Recollect uses the `Proxy` object). Check out the latest usage stats\nfor proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\n## The `collect` function\n\nYou can wrap a React component in `collect` to have Recollect take care of it.\nHere's a component that reads from and writes to the store.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\nimport Task from './Task';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <Task key={task.id} task={task} />\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          id: Math.random(),\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide a store object as a prop\n- Collect information about what data the component needs to render (which parts\n  of the store it reads from).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## The `store` object\n\nYou can import, read from, and write to the store in any file. Or, as you saw\nabove, access it as a prop in a component wrapped in `collect`.\n\nYou don't need to 'create' or 'initialize' this store, it's just there, ready\nwhen you are.\n\nYou can treat the `store` object just like you'd treat any JavaScript object.\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nif ('tasks' in store) {\n  // Nice one\n}\n\ndelete store.tasks; // No problem\n\nstore = 'tasks'; // NOPE! (Can't reassign a constant)\n```\n\nRecollect is always watching and it knows which components need what data from\nthe store, so it will trigger updates accordingly.\n\n---\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n# Advanced usage\n\n- [Advanced usage](#advanced-usage)\n  - [The `afterChange` function](#the-afterchange-function)\n  - [The `batch` function](#the-batch-function)\n  - [The `initStore` function](#the-initstore-function)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [Passing a ref to a collected component](#passing-a-ref-to-a-collected-component)\n  - [Peeking into Recollect's innards](#peeking-into-recollects-innards)\n  - [Usage with TypeScript](#usage-with-typescript)\n    - [Your store](#your-store)\n    - [Using collect](#using-collect)\n- [How Recollect works](#how-recollect-works)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Concepts](#concepts)\n  - [Selectors](#selectors)\n    - [Keeping references to objects in the store](#keeping-references-to-objects-in-the-store)\n    - [Always pass the store to selectors](#always-pass-the-store-to-selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Gotchas with Maps and Sets](#gotchas-with-maps-and-sets)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n## API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n### The `afterChange` function\n\nPass a function to `afterChange` to have it called whenever the store updates.\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\nThe `afterChange` event is an object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g .\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n- `prevStore` — the previous version of the store\n\nThose last two might be interesting if you want to implement time travel, for\nexample:\n\n```js\nimport { afterChange } from 'react-recollect';\n\nconst thePast = [];\nconst theFuture = [];\n\nwindow.TIME_TRAVEL = {\n  back() {\n    if (!thePast.length) return;\n\n    const e = thePast.pop();\n    theFuture.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.prevStore);\n    });\n  },\n  forward() {\n    if (!theFuture.length) return;\n\n    const e = theFuture.pop();\n    thePast.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.store);\n    });\n  },\n};\n\nafterChange((e) => {\n  if (e.renderedComponents.length) thePast.push(e);\n});\n```\n\n### The `batch` function\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan appreciable performance improvement. You can set `__RR__ .debugOn()` to see\nin the console how often your components are being rendered, and why.\n\n### The `initStore` function\n\nWhen you're only using Recollect in the browser, you don't need to 'create' or\n'initialize' the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\nEnter the `initStore` function, which you use on the server and in the browser.\n\n#### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so right\nbefore rendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n#### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n### Passing a ref to a collected component\n\nRefs just work, as long as you don't use the reserved name \"ref\" (React strips\nthis out). You can use something like `inputRef` instead. For an example, see\n[this test](./tests/integration/forwardRefFc.test.tsx)\n\n### Peeking into Recollect's innards\n\nSome neat things are exposed on `window.__RR__` for tinkering in the console.\n\n- Use `__RR__.debugOn()` to turn on debugging. Note that this can have a\n  negative impact on performance if you're reading a _lot_ of data.\n- Type `__RR__.debugOff()` and see what happens\n- `__RR__.internals` returns all sorts of interesting things. Including a live\n  reference to the store. For example, typing\n  `__RR__.internals.store.tasks[1].done = true` in the console would update the\n  store, and Recollect would instruct React to re-render the appropriate\n  components.\n\n## Usage with TypeScript\n\n### Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n### Using collect\n\nComponents wrapped in `collect` must define `store` in `props` - use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# How Recollect works\n\n> This section is for the curious, you don't need to know any of this to use\n> Recollect.\n\nThe `store` object that Recollect exposes is designed to _feel_ like a plain old\nmutable JavaScript object, but it isn't.\n\nIf you do something like `store.site.title = 'Page two'`, Recollect will **not**\nmutate the store object (the `Proxy` object that wraps the store will block the\n`.set()` operation). Instead, it will create a new store where the site title is\n'Page two'. It will then re-render any React components that need to know about\nthe title, passing this _new_ store.\n\nDuring that next render cycle, if a React component looks at `prevProps` inside\n`componentDidUpdate()` it will see the previous version of the store, just like\nyou're used to with state, context, or Redux.\n\nImmediately after the components have re-rendered, the contents of the global\n`store` object are replaced with the contents of the new store. This is all\nsynchronous, so in your code you can treat the store as though it was mutated.\n\nLet's summarise in code:\n\n```js\nstore.site.title = 'Page two';\n\n// - the attempted change is blocked\n// - a new store is created\n// - relevant React components are updated with the new store\n// - the global store object will have its contents replaced with the new store\n// - and then this code will continue to execute...\n\nconsole.log(store.site.title); // 'Page two'. Like you would expect\n```\n\nSo the end result is exactly the same behaviour as a mutable object.\n\nSweet.\n\nHiding away immutability like this allows for simpler code, but there may be\ntimes when you're left scratching your head.\n\nIf you read from the _global_ store object inside the render method of a\ncomponent, you'd actually be getting the previous version of the data, because\nthe 'update components' step comes before the 'update the global store' step.\n\nAnother example:\n\n```js\nconst firstTask = store.tasks[0];\nconst secondTask = store.tasks[1];\n\nstore.tasks[0].done = true;\n\nconsole.log(store.tasks[0].done); // true\n\nconsole.log(firstTask === store.tasks[0]); // false. This task was changed\nconsole.log(secondTask === store.tasks[1]); // true. This task wasn't changed\n```\n\n`firstTask` starts life as a reference to `store.tasks[0]`, but when the store\nis updated, `store.tasks[0]` is _replaced_ with a new version of the task. So it\nis no longer the same thing as `firstTask`.\n\nNote also that Recollect is not just doing a full clone of the store, it only\nclones the object that was changed (and its ancestors), just like Redux\nreducers.\n\nNow for something a bit weird:\n\n```js\nconst firstTask = store.tasks[0];\n\nfirstTask.done = true;\n\nconsole.log(firstTask.done); // false - wot?!\nconsole.log(store.tasks[0].done); // true - double-wot??!!\n```\n\nThis is not so weird when you remember that any attempted change to the store\nwill create a new version of the store, then copy it back into the store object.\nSo when I set `firstTask.done`, Recollect is going to create a new store where\nthat task is done. It doesn't matter if I do `store.tasks[0].done` or\n`firstTask.done` - at the point where I do this they're the same object.\n\nBut when the new version of the store is created, and then written back into the\n`store` object, the link between `store.tasks[0]` and `firstTask` is broken. So\n`firstTask` is still pointing to the original version of the task (where `done`\nis `false`).\n\nThis sucks a bit - no one likes confusing things - but it's necessary to allow\nReact to compare current and previous versions of state (which allows it to\ncleverly not update components where props didn't change).\n\nJust remember:\n\n- you are safe if you read from the `store` object, you will get the most recent\n  version of the store always.\n- deep references to items in the store may be broken if you modify the store.\n  I'd be interested to hear about cases where this is proving unpleasant. Please\n  feel free to open an issue with a code snippet, even if you think it's\n  something that can't be fixed.\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\n## Concepts\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it:\n\n```js\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n```\n\nAnd referencing it in your component:\n\n```jsx\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nApologies for the long variable name if you're reading this on mobile.\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\nSo far none of this has anything to do with Recollect. But there's some\ninteresting stuff happening here that's worth discussing.\n\n### Keeping references to objects in the store\n\nDo you remember when we did this?\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n```\n\nThat `tasks.slice()` part is very important, but first, a history lesson.\n\nWhen the people that made JavaScript were coming up with array methods, for each\none they would do a shot of tequila and flip a coin to decide whether or not it\nshould mutate the original array. So `splice` mutates an array, `slice` does\nnot, `push` does, `concat` does not.\n\nAnd our friend `sort` _does_ mutate the original array.\n\nBut we don't want to change the order of tasks in the store — some other part of\nthe app might be using them. So we `slice()` the array to create a shallow copy.\n'Shallow' means that the tasks in the resulting array are still a reference to\nthe actual tasks in the store. So if we call `task.done = true` on one of them,\nthe store will update.\n\nOr more accurately, when you do `task.done = true` Recollect will orchestrate\nimmutably updating the store and re-render any React components that had read\nthat task's `done` property during their last render.\n\nSo, it's important that if you return an object from a selector (e.g. a task),\nand you want to set a property on that object later (e.g. mark it as done), then\nyou need to make sure you're returning a reference to an item in the store, not\na copy.\n\nAs long as you don't use `Object.assign()` or spread operators or some deep\nclone function, you'll be fine.\n\n### Always pass the store to selectors\n\nFor the same reason as above, you _must_ pass the store to selectors, and it\n_must_ be the store that was passed to the component as a prop (not the one you\ncan import directly from `react-recollect`). If you try to get data from the\nstore imported from `react-recollect`, you will get an error.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\n(Remember, this _looks_ like it's mutating the store, but it ain't.)\n\nYou would reference this from a component by importing it:\n\n```js\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n```\n\nThen calling it in response to some user action:\n\n```jsx\n<button onClick={markAllTasksAsDone}>Mark all as done</button>\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go nuts.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity?\n\nAnd you didn't even need to install\n`react-recollect-immutable-thunk-saga-helper` :)\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## What sort of stuff can go in the store?\n\nData. Anything JSON serializable, plus `Map`, `Set` and `undefined`.\n\nThings that aren't supported:\n\n- functions (e.g. getters, setters, or other methods)\n- properties defined with `Object.defineProperty()`\n- string properties on arrays, Maps and Sets\n- `RegExp` objects\n- `Date` objects (I'm working on this)\n- `Proxy`, `Uint16Array` etc.\n- linking (e.g. one item in the store that is just a reference to another item\n  in the store)\n\n### Gotchas with Maps and Sets\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Will component state still work?\n\nYep. Recollect has no effect on state and the updates triggered as a result of\ncalling `this.setState`.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThe `collect` function wraps your component in a `PureComponent` and there's no\npoint in having two of them.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I use this with `Context`?\n\nThat's a wrong question.\n\nContext is a way to share data across your components. You don't need this now\nthat you have a global `store` object that you can read from and write to\nanywhere and at any time.\n\n## Can I have multiple stores?\n\nNo, but you don't want multiple stores anyway :)\n\nThere is no performance improvement to be had, so the desire for multiple stores\nis just an organizational preference. And objects already have a mechanism to\norganize their contents: 'properties'.\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` - these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 4 KB. If you were to replace `redux`, `redux-thunk`, and\n`react-redux` with this library, you'd shed a bit over 2 KB. But if you've got a\ndecent sized app the real size reduction comes from getting rid of all your\nreducers.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.0-beta.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-5wwNFmiVymE8E36hf07GHpGOXOUwRSQMvAd8MNdkFkEzeckUYo9dQVsqBHh7OVo+nliXRGWh2dlp2SyWufkCXw==","shasum":"f3fe17a5bf97acbf584b0181b5aca997c6a0ec13","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.0-beta.0.tgz","fileCount":3,"unpackedSize":34642,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYEPkCRA9TVsSAnZWagAAWzQP/j5eCMzdawpsnq5j27zJ\n58fq7xMRWXUsZ66hX3UkaxeAZPAW8lhACkeqUR+ihJnJyLawsdpm+XU8npmE\n5TCqbZnDQ+/1igGqYrVSB+XcFj1Mb3jAcznTfcZ/o8odbIL/VRz9CUVDxYBK\nyOb+FQPuqpkqq3u7pP2LdSSdwbA+jVC+CjxDiPCmOb2hFJWgRH8hNkXvODU4\nVqYYTxsjo0pd/on8mwRigqbamTlqZ8mlMIgj4MOn9NVfqEcUhIiJYnGQRreB\n8mS4r2f/KKxQ7BcTyTYIVCxz8ipMjSg/iHdp79wOlSFwmntQZjVEBCD3R44G\nIBD2wopaol/F+OEVfbr+M0mSHoSoh8gp1ij6pnR/9T8Rioek/6esK+TzqXhX\n21hYwh6ph2I+J3v1T1eAOOcgm7+YVMNJapsF7+ET5iIpUJz1LPWgMiC7f3OT\nIhmwCLoYvw6At/vS5h7EbFcKTzvMsin43Ivj03Q0lpWvk7LO5Mnj+7aqppl5\nRgasaLNuR84PhwLx9VvFBTR1xgqGYiAI5R6acfs6p3Gz5Motget0tHDrKJsU\nuzkewW8su8r47lTn/RspqYw1avfgLa6/kRthokl//OIwb62dGjOs7GSPgTp8\nbj2NBE0wm6ykFukRww+q+39aXIF/gQlNZXEQTkebCUz2jOEqSjT+mjwzR15p\nEALy\r\n=Qt7R\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAvUm0LklEUiiPiAPjNPD/9VOO8xadalzuOHsiGc6uL+AiEAqBuI17tq/WPllegXk0Gmx4rhSczeBANTREvFH2tvAv4="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.0-beta.0_1583367139636_0.12385594281515333"},"_hasShrinkwrap":false},"4.0.0-beta.1":{"name":"react-recollect","version":"4.0.0-beta.1","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","test":"jest --watchAll","test:ci":"jest --ci","prepublish":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"48626edebf25bc2d350ea6d14f7cd1ffff82b292","readme":"![Node.js CI](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\nRecollect is a state management library that aims to solve two problems with the\ntraditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed\n\nRecollect solves these problems like so:\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object. No\n   need to worry about accidentally mutating the store; with Recollect that's\n   impossible.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that read\n   that specific property when last rendered is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\nThere is no support for any version of IE, Opera mini, or Android browser 4.4\n(because Recollect uses the `Proxy` object). Check out the latest usage stats\nfor proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\n## The `collect` function\n\nYou can wrap a React component in `collect` to have Recollect take care of it.\nHere's a component that reads from and writes to the store.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\nimport Task from './Task';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <Task key={task.id} task={task} />\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          id: Math.random(),\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide a store object as a prop\n- Collect information about what data the component needs to render (which parts\n  of the store it reads from).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## The `store` object\n\nYou can import, read from, and write to the store in any file. Or, as you saw\nabove, access it as a prop in a component wrapped in `collect`.\n\nYou don't need to 'create' or 'initialize' this store, it's just there, ready\nwhen you are.\n\nYou can treat the `store` object just like you'd treat any JavaScript object.\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nif ('tasks' in store) {\n  // Nice one\n}\n\ndelete store.tasks; // No problem\n\nstore = 'tasks'; // NOPE! (Can't reassign a constant)\n```\n\nRecollect is always watching and it knows which components need what data from\nthe store, so it will trigger updates accordingly.\n\n---\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n# Advanced usage\n\n- [Advanced usage](#advanced-usage)\n  - [The `afterChange` function](#the-afterchange-function)\n  - [The `batch` function](#the-batch-function)\n  - [The `initStore` function](#the-initstore-function)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [Passing a ref to a collected component](#passing-a-ref-to-a-collected-component)\n  - [Peeking into Recollect's innards](#peeking-into-recollects-innards)\n  - [Usage with TypeScript](#usage-with-typescript)\n    - [Your store](#your-store)\n    - [Using collect](#using-collect)\n- [How Recollect works](#how-recollect-works)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Concepts](#concepts)\n  - [Selectors](#selectors)\n    - [Keeping references to objects in the store](#keeping-references-to-objects-in-the-store)\n    - [Always pass the store to selectors](#always-pass-the-store-to-selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Gotchas with Maps and Sets](#gotchas-with-maps-and-sets)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n## API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n### The `afterChange` function\n\nPass a function to `afterChange` to have it called whenever the store updates.\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\nThe `afterChange` event is an object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g .\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n- `prevStore` — the previous version of the store\n\nThose last two might be interesting if you want to implement time travel, for\nexample:\n\n```js\nimport { afterChange } from 'react-recollect';\n\nconst thePast = [];\nconst theFuture = [];\n\nwindow.TIME_TRAVEL = {\n  back() {\n    if (!thePast.length) return;\n\n    const e = thePast.pop();\n    theFuture.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.prevStore);\n    });\n  },\n  forward() {\n    if (!theFuture.length) return;\n\n    const e = theFuture.pop();\n    thePast.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.store);\n    });\n  },\n};\n\nafterChange((e) => {\n  if (e.renderedComponents.length) thePast.push(e);\n});\n```\n\n### The `batch` function\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan appreciable performance improvement. You can set `__RR__ .debugOn()` to see\nin the console how often your components are being rendered, and why.\n\n### The `initStore` function\n\nWhen you're only using Recollect in the browser, you don't need to 'create' or\n'initialize' the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\nEnter the `initStore` function, which you use on the server and in the browser.\n\n#### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so right\nbefore rendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n#### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n### Passing a ref to a collected component\n\nRefs just work, as long as you don't use the reserved name \"ref\" (React strips\nthis out). You can use something like `inputRef` instead. For an example, see\n[this test](./tests/integration/forwardRefFc.test.tsx)\n\n### Peeking into Recollect's innards\n\nSome neat things are exposed on `window.__RR__` for tinkering in the console.\n\n- Use `__RR__.debugOn()` to turn on debugging. Note that this can have a\n  negative impact on performance if you're reading a _lot_ of data.\n- Type `__RR__.debugOff()` and see what happens\n- `__RR__.internals` returns all sorts of interesting things. Including a live\n  reference to the store. For example, typing\n  `__RR__.internals.store.tasks[1].done = true` in the console would update the\n  store, and Recollect would instruct React to re-render the appropriate\n  components.\n\n## Usage with TypeScript\n\n### Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n### Using collect\n\nComponents wrapped in `collect` must define `store` in `props` - use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# How Recollect works\n\n> This section is for the curious, you don't need to know any of this to use\n> Recollect.\n\nThe `store` object that Recollect exposes is designed to _feel_ like a plain old\nmutable JavaScript object, but it isn't.\n\nIf you do something like `store.site.title = 'Page two'`, Recollect will **not**\nmutate the store object (the `Proxy` object that wraps the store will block the\n`.set()` operation). Instead, it will create a new store where the site title is\n'Page two'. It will then re-render any React components that need to know about\nthe title, passing this _new_ store.\n\nDuring that next render cycle, if a React component looks at `prevProps` inside\n`componentDidUpdate()` it will see the previous version of the store, just like\nyou're used to with state, context, or Redux.\n\nImmediately after the components have re-rendered, the contents of the global\n`store` object are replaced with the contents of the new store. This is all\nsynchronous, so in your code you can treat the store as though it was mutated.\n\nLet's summarise in code:\n\n```js\nstore.site.title = 'Page two';\n\n// - the attempted change is blocked\n// - a new store is created\n// - relevant React components are updated with the new store\n// - the global store object will have its contents replaced with the new store\n// - and then this code will continue to execute...\n\nconsole.log(store.site.title); // 'Page two'. Like you would expect\n```\n\nSo the end result is exactly the same behaviour as a mutable object.\n\nSweet.\n\nHiding away immutability like this allows for simpler code, but there may be\ntimes when you're left scratching your head.\n\nIf you read from the _global_ store object inside the render method of a\ncomponent, you'd actually be getting the previous version of the data, because\nthe 'update components' step comes before the 'update the global store' step.\n\nAnother example:\n\n```js\nconst firstTask = store.tasks[0];\nconst secondTask = store.tasks[1];\n\nstore.tasks[0].done = true;\n\nconsole.log(store.tasks[0].done); // true\n\nconsole.log(firstTask === store.tasks[0]); // false. This task was changed\nconsole.log(secondTask === store.tasks[1]); // true. This task wasn't changed\n```\n\n`firstTask` starts life as a reference to `store.tasks[0]`, but when the store\nis updated, `store.tasks[0]` is _replaced_ with a new version of the task. So it\nis no longer the same thing as `firstTask`.\n\nNote also that Recollect is not just doing a full clone of the store, it only\nclones the object that was changed (and its ancestors), just like Redux\nreducers.\n\nNow for something a bit weird:\n\n```js\nconst firstTask = store.tasks[0];\n\nfirstTask.done = true;\n\nconsole.log(firstTask.done); // false - wot?!\nconsole.log(store.tasks[0].done); // true - double-wot??!!\n```\n\nThis is not so weird when you remember that any attempted change to the store\nwill create a new version of the store, then copy it back into the store object.\nSo when I set `firstTask.done`, Recollect is going to create a new store where\nthat task is done. It doesn't matter if I do `store.tasks[0].done` or\n`firstTask.done` - at the point where I do this they're the same object.\n\nBut when the new version of the store is created, and then written back into the\n`store` object, the link between `store.tasks[0]` and `firstTask` is broken. So\n`firstTask` is still pointing to the original version of the task (where `done`\nis `false`).\n\nThis sucks a bit - no one likes confusing things - but it's necessary to allow\nReact to compare current and previous versions of state (which allows it to\ncleverly not update components where props didn't change).\n\nJust remember:\n\n- you are safe if you read from the `store` object, you will get the most recent\n  version of the store always.\n- deep references to items in the store may be broken if you modify the store.\n  I'd be interested to hear about cases where this is proving unpleasant. Please\n  feel free to open an issue with a code snippet, even if you think it's\n  something that can't be fixed.\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\n## Concepts\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it:\n\n```js\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n```\n\nAnd referencing it in your component:\n\n```jsx\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nApologies for the long variable name if you're reading this on mobile.\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\nSo far none of this has anything to do with Recollect. But there's some\ninteresting stuff happening here that's worth discussing.\n\n### Keeping references to objects in the store\n\nDo you remember when we did this?\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n```\n\nThat `tasks.slice()` part is very important, but first, a history lesson.\n\nWhen the people that made JavaScript were coming up with array methods, for each\none they would do a shot of tequila and flip a coin to decide whether or not it\nshould mutate the original array. So `splice` mutates an array, `slice` does\nnot, `push` does, `concat` does not.\n\nAnd our friend `sort` _does_ mutate the original array.\n\nBut we don't want to change the order of tasks in the store — some other part of\nthe app might be using them. So we `slice()` the array to create a shallow copy.\n'Shallow' means that the tasks in the resulting array are still a reference to\nthe actual tasks in the store. So if we call `task.done = true` on one of them,\nthe store will update.\n\nOr more accurately, when you do `task.done = true` Recollect will orchestrate\nimmutably updating the store and re-render any React components that had read\nthat task's `done` property during their last render.\n\nSo, it's important that if you return an object from a selector (e.g. a task),\nand you want to set a property on that object later (e.g. mark it as done), then\nyou need to make sure you're returning a reference to an item in the store, not\na copy.\n\nAs long as you don't use `Object.assign()` or spread operators or some deep\nclone function, you'll be fine.\n\n### Always pass the store to selectors\n\nFor the same reason as above, you _must_ pass the store to selectors, and it\n_must_ be the store that was passed to the component as a prop (not the one you\ncan import directly from `react-recollect`). If you try to get data from the\nstore imported from `react-recollect`, you will get an error.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\n(Remember, this _looks_ like it's mutating the store, but it ain't.)\n\nYou would reference this from a component by importing it:\n\n```js\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n```\n\nThen calling it in response to some user action:\n\n```jsx\n<button onClick={markAllTasksAsDone}>Mark all as done</button>\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go nuts.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity?\n\nAnd you didn't even need to install\n`react-recollect-immutable-thunk-saga-helper` :)\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## What sort of stuff can go in the store?\n\nData. Anything JSON serializable, plus `Map`, `Set` and `undefined`.\n\nThings that aren't supported:\n\n- functions (e.g. getters, setters, or other methods)\n- properties defined with `Object.defineProperty()`\n- string properties on arrays, Maps and Sets\n- `RegExp` objects\n- `Date` objects (I'm working on this)\n- `Proxy`, `Uint16Array` etc.\n- linking (e.g. one item in the store that is just a reference to another item\n  in the store)\n\n### Gotchas with Maps and Sets\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Will component state still work?\n\nYep. Recollect has no effect on state and the updates triggered as a result of\ncalling `this.setState`.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThe `collect` function wraps your component in a `PureComponent` and there's no\npoint in having two of them.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I use this with `Context`?\n\nThat's a wrong question.\n\nContext is a way to share data across your components. You don't need this now\nthat you have a global `store` object that you can read from and write to\nanywhere and at any time.\n\n## Can I have multiple stores?\n\nNo, but you don't want multiple stores anyway :)\n\nThere is no performance improvement to be had, so the desire for multiple stores\nis just an organizational preference. And objects already have a mechanism to\norganize their contents: 'properties'.\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` - these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 4 KB. If you were to replace `redux`, `redux-thunk`, and\n`react-redux` with this library, you'd shed a bit over 2 KB. But if you've got a\ndecent sized app the real size reduction comes from getting rid of all your\nreducers.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.0-beta.1","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-aSvJxXr9jE+3JeljyssGwaUao3tOiGa4WReRLVY6O0SxXqeK2dJqumCIIaGdP1UwvS7XjxnZoFxjku7kO96+PQ==","shasum":"fd1a8b838b73826b6a1f5b41d7edf4cd2501f13e","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.0-beta.1.tgz","fileCount":29,"unpackedSize":127446,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYEZPCRA9TVsSAnZWagAAotUP/2/rq4XRpnxY3rzQekAx\ndiJN1WvzRpsPMbj+raE9+ph24rTTBm/QM0vi7ghpERwTBQKAbgJ7l4qaboJ0\n92p0nvICH9XcaOYlfMKOj1jVGILjRS51jxU+1lbc6OQd//req9RuiDH2v19I\nnRqRlvksE5UAY+MKosQ1sZ5LJ80BcGEQ1HyfbmiGk9H3CUwDnBM1m6qVe6pu\nW7Ol4H0nG0sWd5vqFicjsgcryNjeirz5vDTc0z8M2frS4rtWx4WRaOIj3kf9\nyOnyfDOiG2J4AEZ9jl8Ndx5cQXALe02bZgD/muqnyLG97/WuSKo7KiBG2FNL\nWQr1vGW8m5K/ZqvFVZ8itPFTLIt/S4oacCDNTSJLph2g6bpyXol2Ee4uf3uu\nZWNdAb6IkEqdMtWLv9AsuwwBGAtyCvG44KClXtL5PmUi9qtghhwmj58j6i2a\nqFxdFXK4PgPY2oN8ovdmrz5qDrqC7O9oZxPdzhOBkrnI+6K4MXrSMAMVqQDI\njR384JJGmTkbJGhGaF7atvefDaXbwTfo59J3VJ/5CT5s0slWyzQMgEmV8s+r\nFtYSzz9vCtr/JT/6MP59IWQ/kpuEVUcq2ZcOtlril8ZQ3YQsANPSFvHJgvhi\nTNh5DuGCyH3Ly0gKuwO6m5p1fLXFxsDJFQMvBeS6SS/SyiFasIl2d8Tx2ww8\nOQHg\r\n=sVEe\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE6Qz4bD3YcVy7HSy2BlZzySSaZOSSQOjayeTKgL/ALzAiEA2yVQAOSkRv1dygFY+kAs/sePde4602jDgG7MvSs5YvI="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.0-beta.1_1583367758847_0.5251298245099087"},"_hasShrinkwrap":false},"4.0.0":{"name":"react-recollect","version":"4.0.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","test":"jest --watchAll","test:ci":"jest --ci","prepublish":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"719bd9f3ed0557f11660f55b70797fe0ca053aa2","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-kjCTe9eV7EVIrDnL7mcfLXWPUTk+FdzfcwJwwfK9SCaL9IsHHQsVB8lKlsoaOjGD63eeSY8xzTA/R3xjmaK+sg==","shasum":"518aaca9fd40a69997210f8a2a1d6f78945030e5","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.0.tgz","fileCount":29,"unpackedSize":127439,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYGMrCRA9TVsSAnZWagAAsDgP/A/wxaQe5ggN7iQmpMXH\nZ2M7jK1dOiLIBQdLSHkT1Gl4Smravwysm6FrfNW4mSdx4Qr1Nggf6GUOHaZW\nClmJRrN2sxA12nmebDh5NP2OLyBdH2ug4+PxJjcCRD4g/ToDTVcGX8wF5bUF\n0FbLoDia3kZey3EXx3+9VnVurspcYgHnFC3h0+p8/47y5Ykkkd9VtIcmKhY0\nV1+Vn9/MXNorYugEhZx9qZ6V5oOOeuEB0XfDB9buWUvifdn4il1iCgtV1meQ\nRfbmM86mPedzXXvZ9lYockcnxKk6m1Kki9MJavZqMooz+SFWWRCbPuf2/OWl\nWiIOFdLpCEEIumFDcLhQoPVEL4YbEUJQjPiC5FX/+ploEJE8y0CXYnx3PGan\njrmFkUC2N10dX7zTMey3wHZlCaWMIe9/nsJxDMRK116rwV9x0ZulFsaK9VRf\noVPStQhJsolejf8EcnPBOzqwvgFbkMXnTHt15CMHhWIFLUg/4IZPfUoCeMSp\nfVMCZrdA7nIh12+F2fDeaG3uf0N+FUhiw1Xx1zjT2mULDiK8ISGKmdcmRAP7\nrEZID028LF6wed5pcbTEuGtVRPpDqrYXXds08rwVelgVCBKJXuORRhexZXGy\nGuiGx2JrbEthrFiSK1oJOef2h2i/yKHC8CYNPAjRE8TXW0hnOOX2tyTFbmH8\nGJZ0\r\n=YDE3\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDDEvq05h37s1X08p0oqEsD66B0B/1nQpfYeKGV5icKZQIhAOFfd4jzrK9LxmlLsXXF7ETPCA/r0bZE+1lZehekmETH"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.0_1583375147106_0.44212753639225943"},"_hasShrinkwrap":false},"4.0.1":{"name":"react-recollect","version":"4.0.1","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"50dda4885931150d1f49807323b9461a6667f130","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.1","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-yh8SbbkrE9HyiY+37sI/gWzJS3RorY8leGYsF2a6ddttvN/Kg1DpSTkTfUwjqQ6FHQh1JJupDG/yW+BcLBZqNg==","shasum":"9a883b7c71f08ed34d5df28f0c8145baf3cab173","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.1.tgz","fileCount":29,"unpackedSize":127046,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYK5UCRA9TVsSAnZWagAABbAQAIvjxUqfn1/ZOtPOuQav\nVjDZJ3M0wnVouU3NOfFK8ei0X+2o7OjHF3+tIsXtI5O2vCMfubEgTEjZclga\no53RAKkaiUUapUt7yuwHCjfKK5HEojNAO8zQeQYMxHpWXJ7+QUeI9x/Idsi0\n2qBPQ7muUSTNKEJMtaJYtM0jTP1r13n//tIjLiWus7745n1PsM9Sba2ZoaG1\nDRqMCN0CGj7Nrl1U2B1HyovUzN7zYsibSO2E3RfAudvLD3tncQBk71GJh4Gu\nRDFN2CbgBqrFDDpmTEjPX4Y29rR3vl7vCPSgRUphLPRZWisZPrcEQSqTVkuz\nbopKQDiorbUf+1PKUxunphZIBqMrqlN5dyGjvNmvoaj5P3d/JdZYIkZh+i4+\nWDzSvyachz+/x8Og9oNxij9hoS4AX2gUGqcroSBxpMvyTngNCsh8ychiJWfg\nz2XPgIRLIxriWaS8Ft9sqaOG86W8A4WiWUdOAjb77+HWfpsgW0USyxoDf3Za\nUdmBfRSXfnNlCYcY9hM5+WKLfSJQGT/YKKKSfnrL1f39DfqpU5wBU3mhQ7+I\ntYO4kHC8HDn2IzhizTEIl8QJmJbLEo91xA60Z8NvEq8GGBqDhtOioDg+p0Nq\nzOnwibejPRORFmHw/86gYuCkWIJgJuXph6/Kpg3KKH4onSowFMUFjric42wt\njY1x\r\n=b67j\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCSNKiDbWTO2Ei1QMeVfXG0N8bpDFn3SaKU0VIVpNuDowIhAOjALd1D/lDkqYdzdxwtgmn9PqFzS1pZMALDCm/HIeVS"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.1_1583394387300_0.5433453556804639"},"_hasShrinkwrap":false},"4.0.2":{"name":"react-recollect","version":"4.0.2","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"6b899febce780e7b64a8724e5c9711c48615ab10","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.2","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-F7RSrcQ0qB8tXtMsV+JIkBzuQUrO0uhbTyjFxSQaX4QQ3ipDs62vSmT6Jf0uW+9P997DxLfteUbkQokVwO4Qcw==","shasum":"341c4edaeb32d69067513ecd8f233b9e34a7f4d6","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.2.tgz","fileCount":29,"unpackedSize":127374,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeYzyUCRA9TVsSAnZWagAAVuwP/jLh3SYbf0Y3hbC+nuPz\nQYfuglt+HrpmY3+AdKB7bGly3MRfnDUzC5VEapdS5/9zxWjkkJzjILW1Ho0I\nT4mBYG6RhgiIU1x1BGhaZ63MWvU3W5p57NDE/J6gqzViO8/AJVq+KMXi/sOt\n67LDBqAjXnDjvvpk6snw/lc9efHCu66uXhF+7CPs4criXC2sdBdUg7VrDvDM\n5pfS2hLoyTudDO8oA9+1Y7mpHaUxfSjXPp9pG+B6siF0Xq8JoWN/yNlU+x+g\n1vY7k2wKLjqFKLZh5MjJYq163DmP96pm7TnmW1j9wuDcuMyp+RNGE9td7pro\nJwJ/bCNshELnUtP3zUy1RwpEe2X84/7uzDnuRoHad42Arp0HiqSUmh/gy+nT\nOCw+ncKlb6gt8yRYLQowIspCZYNVRkXgJ1HLA3ExEVJZWkeSrfQfCwXV7A7Q\n6AtjbCkvUeKYs8ox1pYddUYhWAsh4oZIjscEXDvZLl6K2I+o0c5x1dEarTWI\n9dR41u8pF6qGdVStgUg7m5aDDMJ4JR1FQzD5/IRp0wrZG9c3h/lBfLWPCMys\noAYRlJdYkiliIuhRSSDhDkWuj93btq/boyXRxwfJXHmwIF2qcGvc8bZOjawC\n7Ni9sPu3H7DKyYY82fhG+EgcfBoFByVJYJGcuQU4iFlmWN7yn75HykkqUh5R\n9SdI\r\n=7EKY\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIGo2Jp1vYlEDXEYJh196sXFrF3RqeMosU3LsmVXdtRqzAiEAvnLTjDNgrxTdd75UvLUia6QvBLWy3WMdVm9lzG9kh+4="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.2_1583561875570_0.950414616063137"},"_hasShrinkwrap":false},"4.0.3":{"name":"react-recollect","version":"4.0.3","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"f9422831fbfed856d7ad3083194aa51a5877c207","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.3","_nodeVersion":"13.5.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-sZhhQFaZ7d4I38rynIIn+U9AVBws7MlalKXOVil+JC6cPovTDmjO760WNRe+qYxVS05cEtJUeXwOanNQKgp+BA==","shasum":"db6a3ef5d40fdf9b0fefd07b55db85b1b5d856a8","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.3.tgz","fileCount":29,"unpackedSize":127610,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZflHCRA9TVsSAnZWagAAr4gQAIkUa2U5SiWUO4m86LkS\nSLZkUmS+z7tn5K55enzKDaCoRHUyTocJl+9OkQAFWVtCZylE5yXIb8FC0eBU\nnMRTeBvUfRj/xPUjpssZD6/xdfNz8zlnJMuA3DmClPNdpAXqehneNqFMoEsd\n9gHsNrMdydqmeZXKpycuE6Jk2t1KIGR8/UnjgVtpUw20qw8t0ZhbPfGMj48N\nHO9a9uMhV8N0Sl9rdLsDQHaKhU/FpbsEpT5TqUPdmseRNRgTtIFlW+uxkjzo\nCtgzGAh4tT05iZT7CymS0wIXPxdqfqVrG9GASLn+anXW2XS19CjI6nxScmVO\nwpNSGm409XU5P7FQN5/k6PhkKGbz0XEf3u+xmyM5hFcSwA7wfROG1i6WN+qz\nY7dYuJerHPVu66teTtcpmVbstMSyXQUI6kqXDyyhdFbcIHCnDBIKrY9w7GrR\nYH3G1Ql8pDttzKQWdJfTzrRENA5farK482a23iZrF0gdXbBIm2/yioTRhKDa\nfc64pAz64nt3GyFumQoKRH4Anlh92NZYIZGyGjxfoY3r+pJ8VkGW98TWceFz\n5UpnS8c+8C4ZUqpWbTY7HkVCiEYXEWK+qybAiJl82rATbiLCDYiViKJZy2/Y\n6aO5I3ixQDMh5IGLJgx25EotAkFCGEiqIezf7JX97g6jeVUYJL8vnYDUiOgu\nO2gq\r\n=kAMu\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDUm/qeOIw2hXPKAg4jERUhmzIaNdM72cwyv0jIzeKa8AIhAKtqaL1HyMLIGBGGRL5RQlpJS7/OWJf1s7b+B0VmUm+q"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.3_1583741255173_0.3000164333910289"},"_hasShrinkwrap":false},"4.0.4":{"name":"react-recollect","version":"4.0.4","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"6cb66a0de547238c998de32f228e1e0c3452ba09","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@4.0.4","_nodeVersion":"13.5.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-EY8H0G+qozhXaQotsmxJvf56wLb4yNsMzZk5HwuT5VXo3sNBOBLfEQ1a7/LYgVW/UnUJAE0RttHTK+Jzoz87Wg==","shasum":"63771625ba8330b93127e5386ff0315e7a5a2297","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-4.0.4.tgz","fileCount":29,"unpackedSize":129230,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeZv89CRA9TVsSAnZWagAAlNcQAI4gTaFfDM3Ss7ZNP+0f\ng7M0zf64WtEgcf+aX81tslpwoTTI9Ad91GprdxP3ZFkNA5ENFD6aY7I2tdzo\n36m34+5A71v9sJBgky9RZGHd62y/L1QsvlErIpX6dcPrzraCTHob+x2behdQ\na3koBhV9H8ttQAhw6UuxhKoA26PWwA/sOwBznJf7Ra7z0k1HFWoKhBIkxT9P\nvNas4WhF8SwybxeVK4d5gCwjrx1mx1EsjbAxVBsotAo+oj+/DCOHn/rYkjPn\nTNaxLZUeFLuusG2OCPyANytX8GQGpjoUrMYSzqSy3eAE1i5OuBFNzhRfDU4H\nsk/LqsqvIU+GrIQ2oClpe4Y9/OqOt8Ausyqbpx73vQ8SQREGAzOndYav3x1g\nke50o71uRd9bPhpMq+D1bSViDltL/2iBA6+/BsZi6qD0ZieiWc9SXB0qwRGz\n8FaS61HSLAqJLVmyc1OBHDsp9A+5n+4RzWkKcu1RI6jOcuCNKWhh0bQ4i37g\naXxiovc+LBxokjTJfJHNXOhmJZObYbQIJpjMaxjFGUJ8H41zgGWzYRHfsNDd\nDrtW72SPHjOpErnCkXwYhzo1pv3SLU5HpXk1ShORHsa+V6SrUHbmVDnyitIG\nG5SkgoeZiwbKGD4bzuXRcACFjr3QrlLFlHejPWgiyEhnZhaqcAYK1q8KfhDr\nLuyN\r\n=B39/\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDke2wtiWQwazO1XxQ4S2DqYglPtM/CYqeOVsmNdZXRnwIhAPZbqbz4lF+itIDHtRWK7w9+m5X5lr4zdnJtsdEqngJr"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_4.0.4_1583808317087_0.5052127558794699"},"_hasShrinkwrap":false},"5.0.1-beta.0":{"name":"react-recollect","version":"5.0.1-beta.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"b3e0b945a1a783ba9469ac14ea51ac755c295ea4","readme":"![Node.js CI](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\nRecollect is a state management library that aims to solve two problems with the\ntraditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed\n\nRecollect solves these problems like so:\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object. No\n   need to worry about accidentally mutating the store; with Recollect that's\n   impossible.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that read\n   that specific property when last rendered is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\nThere is no support for any version of IE, Opera mini, or Android browser 4.4\n(because Recollect uses the `Proxy` object). Check out the latest usage stats\nfor proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\n## The `collect` function\n\nYou can wrap a React component in `collect` to have Recollect take care of it.\nHere's a component that reads from and writes to the store.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\nimport Task from './Task';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <Task key={task.id} task={task} />\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          id: Math.random(),\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide a store object as a prop\n- Collect information about what data the component needs to render (which parts\n  of the store it reads from).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## The `store` object\n\nYou can import, read from, and write to the store in any file. Or, as you saw\nabove, access it as a prop in a component wrapped in `collect`.\n\nYou don't need to 'create' or 'initialize' this store, it's just there, ready\nwhen you are.\n\nYou can treat the `store` object just like you'd treat any JavaScript object.\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nif ('tasks' in store) {\n  // Nice one\n}\n\ndelete store.tasks; // No problem\n\nstore = 'tasks'; // NOPE! (Can't reassign a constant)\n```\n\nRecollect is always watching and it knows which components need what data from\nthe store, so it will trigger updates accordingly.\n\n---\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n# Advanced usage\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n\n  - [API](#api)\n    - [The `afterChange` function](#the-afterchange-function)\n    - [The `batch` function](#the-batch-function)\n    - [The `initStore` function](#the-initstore-function)\n      - [On the server](#on-the-server)\n      - [In the browser](#in-the-browser)\n    - [Passing a ref to a collected component](#passing-a-ref-to-a-collected-component)\n    - [Peeking into Recollect's innards](#peeking-into-recollects-innards)\n  - [Usage with TypeScript](#usage-with-typescript)\n    - [Your store](#your-store)\n    - [Using collect](#using-collect)\n- [How Recollect works](#how-recollect-works)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Concepts](#concepts)\n  - [Selectors](#selectors)\n    - [Keeping references to objects in the store](#keeping-references-to-objects-in-the-store)\n    - [Always pass the store to selectors](#always-pass-the-store-to-selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Gotchas with Maps and Sets](#gotchas-with-maps-and-sets)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n  - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n### The `afterChange` function\n\nPass a function to `afterChange` to have it called whenever the store updates.\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\nThe `afterChange` event is an object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g .\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n- `prevStore` — the previous version of the store\n\nThose last two might be interesting if you want to implement time travel, for\nexample:\n\n```js\nimport { afterChange } from 'react-recollect';\n\nconst thePast = [];\nconst theFuture = [];\n\nwindow.TIME_TRAVEL = {\n  back() {\n    if (!thePast.length) return;\n\n    const e = thePast.pop();\n    theFuture.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.prevStore);\n    });\n  },\n  forward() {\n    if (!theFuture.length) return;\n\n    const e = theFuture.pop();\n    thePast.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.store);\n    });\n  },\n};\n\nafterChange((e) => {\n  if (e.renderedComponents.length) thePast.push(e);\n});\n```\n\n### The `batch` function\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan appreciable performance improvement. You can set `__RR__ .debugOn()` to see\nin the console how often your components are being rendered, and why.\n\n### The `initStore` function\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in. If you don't pass anything, it will empty the store.\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n#### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so right\nbefore rendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n#### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n### Passing a ref to a collected component\n\nRefs just work, as long as you don't use the reserved name \"ref\" (React strips\nthis out). You can use something like `inputRef` instead. For an example, see\n[this test](./tests/integration/forwardRefFc.test.tsx)\n\n### Peeking into Recollect's innards\n\nSome neat things are exposed on `window.__RR__` for tinkering in the console.\n\n- Use `__RR__.debugOn()` to turn on debugging. Note that this can have a\n  negative impact on performance if you're reading a _lot_ of data.\n- Type `__RR__.debugOff()` and see what happens\n- `__RR__.internals` returns all sorts of interesting things. Including a live\n  reference to the store. For example, typing\n  `__RR__.internals.store.tasks[1].done = true` in the console would update the\n  store, and Recollect would instruct React to re-render the appropriate\n  components.\n\n## Usage with TypeScript\n\n### Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n### Using collect\n\nComponents wrapped in `collect` must define `store` in `props` - use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# How Recollect works\n\n> This section is for the curious, you don't need to know any of this to use\n> Recollect.\n\nThe `store` object that Recollect exposes is designed to _feel_ like a plain old\nmutable JavaScript object, but it isn't.\n\nIf you do something like `store.site.title = 'Page two'`, Recollect will **not**\nmutate the store object (the `Proxy` object that wraps the store will block the\n`.set()` operation). Instead, it will create a new store where the site title is\n'Page two'. It will then re-render any React components that need to know about\nthe title, passing this _new_ store.\n\nDuring that next render cycle, if a React component looks at `prevProps` inside\n`componentDidUpdate()` it will see the previous version of the store, just like\nyou're used to with state, context, or Redux.\n\nImmediately after the components have re-rendered, the contents of the global\n`store` object are replaced with the contents of the new store. This is all\nsynchronous, so in your code you can treat the store as though it was mutated.\n\nLet's summarise in code:\n\n```js\nstore.site.title = 'Page two';\n\n// - the attempted change is blocked\n// - a new store is created\n// - relevant React components are updated with the new store\n// - the global store object will have its contents replaced with the new store\n// - and then this code will continue to execute...\n\nconsole.log(store.site.title); // 'Page two'. Like you would expect\n```\n\nSo the end result is exactly the same behaviour as a mutable object.\n\nSweet.\n\nHiding away immutability like this allows for simpler code, but there may be\ntimes when you're left scratching your head.\n\nIf you read from the _global_ store object inside the render method of a\ncomponent, you'd actually be getting the previous version of the data, because\nthe 'update components' step comes before the 'update the global store' step.\n\nAnother example:\n\n```js\nconst firstTask = store.tasks[0];\nconst secondTask = store.tasks[1];\n\nstore.tasks[0].done = true;\n\nconsole.log(store.tasks[0].done); // true\n\nconsole.log(firstTask === store.tasks[0]); // false. This task was changed\nconsole.log(secondTask === store.tasks[1]); // true. This task wasn't changed\n```\n\n`firstTask` starts life as a reference to `store.tasks[0]`, but when the store\nis updated, `store.tasks[0]` is _replaced_ with a new version of the task. So it\nis no longer the same thing as `firstTask`.\n\nNote also that Recollect is not just doing a full clone of the store, it only\nclones the object that was changed (and its ancestors), just like Redux\nreducers.\n\nNow for something a bit weird:\n\n```js\nconst firstTask = store.tasks[0];\n\nfirstTask.done = true;\n\nconsole.log(firstTask.done); // false - wot?!\nconsole.log(store.tasks[0].done); // true - double-wot??!!\n```\n\nThis is not so weird when you remember that any attempted change to the store\nwill create a new version of the store, then copy it back into the store object.\nSo when I set `firstTask.done`, Recollect is going to create a new store where\nthat task is done. It doesn't matter if I do `store.tasks[0].done` or\n`firstTask.done` - at the point where I do this they're the same object.\n\nBut when the new version of the store is created, and then written back into the\n`store` object, the link between `store.tasks[0]` and `firstTask` is broken. So\n`firstTask` is still pointing to the original version of the task (where `done`\nis `false`).\n\nThis sucks a bit - no one likes confusing things - but it's necessary to allow\nReact to compare current and previous versions of state (which allows it to\ncleverly not update components where props didn't change).\n\nJust remember:\n\n- you are safe if you read from the `store` object, you will get the most recent\n  version of the store always.\n- deep references to items in the store may be broken if you modify the store.\n  I'd be interested to hear about cases where this is proving unpleasant. Please\n  feel free to open an issue with a code snippet, even if you think it's\n  something that can't be fixed.\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\n## Concepts\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it:\n\n```js\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n```\n\nAnd referencing it in your component:\n\n```jsx\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nApologies for the long variable name if you're reading this on mobile.\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\nSo far none of this has anything to do with Recollect. But there's some\ninteresting stuff happening here that's worth discussing.\n\n### Keeping references to objects in the store\n\nDo you remember when we did this?\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n```\n\nThat `tasks.slice()` part is very important, but first, a history lesson.\n\nWhen the people that made JavaScript were coming up with array methods, for each\none they would do a shot of tequila and flip a coin to decide whether or not it\nshould mutate the original array. So `splice` mutates an array, `slice` does\nnot, `push` does, `concat` does not.\n\nAnd our friend `sort` _does_ mutate the original array.\n\nBut we don't want to change the order of tasks in the store — some other part of\nthe app might be using them. So we `slice()` the array to create a shallow copy.\n'Shallow' means that the tasks in the resulting array are still a reference to\nthe actual tasks in the store. So if we call `task.done = true` on one of them,\nthe store will update.\n\nOr more accurately, when you do `task.done = true` Recollect will orchestrate\nimmutably updating the store and re-render any React components that had read\nthat task's `done` property during their last render.\n\nSo, it's important that if you return an object from a selector (e.g. a task),\nand you want to set a property on that object later (e.g. mark it as done), then\nyou need to make sure you're returning a reference to an item in the store, not\na copy.\n\nAs long as you don't use `Object.assign()` or spread operators or some deep\nclone function, you'll be fine.\n\n### Always pass the store to selectors\n\nFor the same reason as above, you _must_ pass the store to selectors, and it\n_must_ be the store that was passed to the component as a prop (not the one you\ncan import directly from `react-recollect`). If you try to get data from the\nstore imported from `react-recollect`, you will get an error.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\n(Remember, this _looks_ like it's mutating the store, but it ain't.)\n\nYou would reference this from a component by importing it:\n\n```js\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n```\n\nThen calling it in response to some user action:\n\n```jsx\n<button onClick={markAllTasksAsDone}>Mark all as done</button>\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go nuts.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity?\n\nAnd you didn't even need to install\n`react-recollect-immutable-thunk-saga-helper` :)\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## What sort of stuff can go in the store?\n\nData. Anything JSON serializable, plus `Map`, `Set` and `undefined`.\n\nThings that aren't supported:\n\n- functions (e.g. getters, setters, or other methods)\n- properties defined with `Object.defineProperty()`\n- string properties on arrays, Maps and Sets\n- `RegExp` objects\n- `Date` objects (I'm working on this)\n- `Proxy`, `Uint16Array` etc.\n- linking (e.g. one item in the store that is just a reference to another item\n  in the store)\n\n### Gotchas with Maps and Sets\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYep. Recollect has no effect on state and the updates triggered as a result of\ncalling `this.setState` or changes via `useState`.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n## Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nIssue: https://github.com/davidgilbertson/react-recollect/issues/85\n\nThere are two workarounds:\n\n1. Use React's `useEffect` hook (React 16.8+). See\n   [these tests](./tests/integration/componentDidUpdate.test.tsx)\n2. Pass in `loaded` as a prop from the parent component.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nBut it's generally a good idea to wrap other components in `PureComponent` or\n`React.memo` - especially components that are rendered in an array, like\n`<Todo>`. If you have a hundred todos, and add one to the list, you can skip a\nrender for all the existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nThat's a wrong question.\n\nContext is a way to share data across your components. You don't need this now\nthat you have a global `store` object that you can read from and write to\nanywhere and at any time.\n\n## Can I have multiple stores?\n\nNo, but you don't want multiple stores anyway :)\n\nThere is no performance improvement to be had, so the desire for multiple stores\nis just an organizational preference. And objects already have a mechanism to\norganize their contents: 'properties'.\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` - these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 4 KB. If you were to replace `redux`, `redux-thunk`, and\n`react-redux` with this library, you'd shed a bit over 2 KB. But if you've got a\ndecent sized app the real size reduction comes from getting rid of all your\nreducers.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.0.1-beta.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-0dQDja66PudX0S2A3OvtpCLbpkz5eAl4lXM8FP4V9gSX77Y9v2mPq2TlXXxUzldYMAB6P//bsCOgBhy7Tyvqig==","shasum":"51d5c08d52396b96455d06cd44eeee55f1e4ec2a","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.0.1-beta.0.tgz","fileCount":33,"unpackedSize":132863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeba9NCRA9TVsSAnZWagAAbpcP/0JRF5NRLOlZG60fbDiE\nZYQOO+jrp+RJm3oqtyLeWmHqmk/d1kmdrX1TBwj3v4Q6pRzxxo1mX+YERoQ+\ninU+pmx8cRH7l8XSo8BwivDck02MzdnZsI7FHSxWZc3CtwQW9x7BGjN5l/w4\n/jiNt1XHfey1226uNal4j0hLe77ft19l9Z+vGnF8zf5bgzuV8A8/1noz7Y3A\n1nxwZ2agl+uL9Cfkj6cBJ56xlzvOfNWnB18FOkX0HnfEl12gdq37mdV7DgzI\nrTrjI3vIy0Zs7pbhG677Ec3GEv3whLCAARIlTf0Bhhymecem8yIq3QnbxiXT\noDgoni/voTemezVdMsfX+4LUTJb2WnzKDXyZrFm60UdzRx0w4RLQUirQ/hOL\nrQqfKjoS2qKGc+ovsjZKX0XvLmdTSFM57D36bO3gYlkT6KY9zoOrKNTdhM9Q\nHYX0HtBTcK3pmyMwjqquZ21gyn9tI1nkLRBhBqX52vr5cLT7Y6Ys3O/KqnIe\nGP5QwHu9igp39MUwwHuYVw2E/Ntmyr4MOWLTGA3C5C48ixPsYUjHrkjcWMxr\nK1V5VUpKbal6t1W7zAFvEuRdEClFj27krumHOxCW/Ga56EqfI+UbcjMiYPyj\nHCzOv3OWVjrCl1lnAfuyjUBe8rSQHexhU5lSOmSx52MHzsl5ykL/joy8ijb/\nGam0\r\n=o6bs\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH+QqFCUwwmq2SnHB3BlZ4vll031C5aHBkOWh/Dsy97LAiEA3AWke0db3uxTBGl6YMcOeadluBI9pC4neSO5Lxg413A="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.0.1-beta.0_1584246605504_0.011995483154175979"},"_hasShrinkwrap":false},"5.0.0-beta.0":{"name":"react-recollect","version":"5.0.0-beta.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"1c157846b06645de05088d1f9a47e63980bd082b","readme":"![Node.js CI](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\nRecollect is a state management library that aims to solve two problems with the\ntraditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed\n\nRecollect solves these problems like so:\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object. No\n   need to worry about accidentally mutating the store; with Recollect that's\n   impossible.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that read\n   that specific property when last rendered is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\nThere is no support for any version of IE, Opera mini, or Android browser 4.4\n(because Recollect uses the `Proxy` object). Check out the latest usage stats\nfor proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\n## The `collect` function\n\nYou can wrap a React component in `collect` to have Recollect take care of it.\nHere's a component that reads from and writes to the store.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\nimport Task from './Task';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <Task key={task.id} task={task} />\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          id: Math.random(),\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide a store object as a prop\n- Collect information about what data the component needs to render (which parts\n  of the store it reads from).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## The `store` object\n\nYou can import, read from, and write to the store in any file. Or, as you saw\nabove, access it as a prop in a component wrapped in `collect`.\n\nYou don't need to 'create' or 'initialize' this store, it's just there, ready\nwhen you are.\n\nYou can treat the `store` object just like you'd treat any JavaScript object.\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nif ('tasks' in store) {\n  // Nice one\n}\n\ndelete store.tasks; // No problem\n\nstore = 'tasks'; // NOPE! (Can't reassign a constant)\n```\n\nRecollect is always watching and it knows which components need what data from\nthe store, so it will trigger updates accordingly.\n\n---\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n# Advanced usage\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n\n  - [API](#api)\n    - [The `afterChange` function](#the-afterchange-function)\n    - [The `batch` function](#the-batch-function)\n    - [The `initStore` function](#the-initstore-function)\n      - [On the server](#on-the-server)\n      - [In the browser](#in-the-browser)\n    - [Passing a ref to a collected component](#passing-a-ref-to-a-collected-component)\n    - [Peeking into Recollect's innards](#peeking-into-recollects-innards)\n  - [Usage with TypeScript](#usage-with-typescript)\n    - [Your store](#your-store)\n    - [Using collect](#using-collect)\n- [How Recollect works](#how-recollect-works)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Concepts](#concepts)\n  - [Selectors](#selectors)\n    - [Keeping references to objects in the store](#keeping-references-to-objects-in-the-store)\n    - [Always pass the store to selectors](#always-pass-the-store-to-selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Gotchas with Maps and Sets](#gotchas-with-maps-and-sets)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n  - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n### The `afterChange` function\n\nPass a function to `afterChange` to have it called whenever the store updates.\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\nThe `afterChange` event is an object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g .\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n- `prevStore` — the previous version of the store\n\nThose last two might be interesting if you want to implement time travel, for\nexample:\n\n```js\nimport { afterChange } from 'react-recollect';\n\nconst thePast = [];\nconst theFuture = [];\n\nwindow.TIME_TRAVEL = {\n  back() {\n    if (!thePast.length) return;\n\n    const e = thePast.pop();\n    theFuture.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.prevStore);\n    });\n  },\n  forward() {\n    if (!theFuture.length) return;\n\n    const e = theFuture.pop();\n    thePast.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.store);\n    });\n  },\n};\n\nafterChange((e) => {\n  if (e.renderedComponents.length) thePast.push(e);\n});\n```\n\n### The `batch` function\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan appreciable performance improvement. You can set `__RR__ .debugOn()` to see\nin the console how often your components are being rendered, and why.\n\n### The `initStore` function\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in. If you don't pass anything, it will empty the store.\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n#### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so right\nbefore rendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n#### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n### Passing a ref to a collected component\n\nRefs just work, as long as you don't use the reserved name \"ref\" (React strips\nthis out). You can use something like `inputRef` instead. For an example, see\n[this test](./tests/integration/forwardRefFc.test.tsx)\n\n### Peeking into Recollect's innards\n\nSome neat things are exposed on `window.__RR__` for tinkering in the console.\n\n- Use `__RR__.debugOn()` to turn on debugging. Note that this can have a\n  negative impact on performance if you're reading a _lot_ of data.\n- Type `__RR__.debugOff()` and see what happens\n- `__RR__.internals` returns all sorts of interesting things. Including a live\n  reference to the store. For example, typing\n  `__RR__.internals.store.tasks[1].done = true` in the console would update the\n  store, and Recollect would instruct React to re-render the appropriate\n  components.\n\n## Usage with TypeScript\n\n### Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n### Using collect\n\nComponents wrapped in `collect` must define `store` in `props` - use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# How Recollect works\n\n> This section is for the curious, you don't need to know any of this to use\n> Recollect.\n\nThe `store` object that Recollect exposes is designed to _feel_ like a plain old\nmutable JavaScript object, but it isn't.\n\nIf you do something like `store.site.title = 'Page two'`, Recollect will **not**\nmutate the store object (the `Proxy` object that wraps the store will block the\n`.set()` operation). Instead, it will create a new store where the site title is\n'Page two'. It will then re-render any React components that need to know about\nthe title, passing this _new_ store.\n\nDuring that next render cycle, if a React component looks at `prevProps` inside\n`componentDidUpdate()` it will see the previous version of the store, just like\nyou're used to with state, context, or Redux.\n\nImmediately after the components have re-rendered, the contents of the global\n`store` object are replaced with the contents of the new store. This is all\nsynchronous, so in your code you can treat the store as though it was mutated.\n\nLet's summarise in code:\n\n```js\nstore.site.title = 'Page two';\n\n// - the attempted change is blocked\n// - a new store is created\n// - relevant React components are updated with the new store\n// - the global store object will have its contents replaced with the new store\n// - and then this code will continue to execute...\n\nconsole.log(store.site.title); // 'Page two'. Like you would expect\n```\n\nSo the end result is exactly the same behaviour as a mutable object.\n\nSweet.\n\nHiding away immutability like this allows for simpler code, but there may be\ntimes when you're left scratching your head.\n\nIf you read from the _global_ store object inside the render method of a\ncomponent, you'd actually be getting the previous version of the data, because\nthe 'update components' step comes before the 'update the global store' step.\n\nAnother example:\n\n```js\nconst firstTask = store.tasks[0];\nconst secondTask = store.tasks[1];\n\nstore.tasks[0].done = true;\n\nconsole.log(store.tasks[0].done); // true\n\nconsole.log(firstTask === store.tasks[0]); // false. This task was changed\nconsole.log(secondTask === store.tasks[1]); // true. This task wasn't changed\n```\n\n`firstTask` starts life as a reference to `store.tasks[0]`, but when the store\nis updated, `store.tasks[0]` is _replaced_ with a new version of the task. So it\nis no longer the same thing as `firstTask`.\n\nNote also that Recollect is not just doing a full clone of the store, it only\nclones the object that was changed (and its ancestors), just like Redux\nreducers.\n\nNow for something a bit weird:\n\n```js\nconst firstTask = store.tasks[0];\n\nfirstTask.done = true;\n\nconsole.log(firstTask.done); // false - wot?!\nconsole.log(store.tasks[0].done); // true - double-wot??!!\n```\n\nThis is not so weird when you remember that any attempted change to the store\nwill create a new version of the store, then copy it back into the store object.\nSo when I set `firstTask.done`, Recollect is going to create a new store where\nthat task is done. It doesn't matter if I do `store.tasks[0].done` or\n`firstTask.done` - at the point where I do this they're the same object.\n\nBut when the new version of the store is created, and then written back into the\n`store` object, the link between `store.tasks[0]` and `firstTask` is broken. So\n`firstTask` is still pointing to the original version of the task (where `done`\nis `false`).\n\nThis sucks a bit - no one likes confusing things - but it's necessary to allow\nReact to compare current and previous versions of state (which allows it to\ncleverly not update components where props didn't change).\n\nJust remember:\n\n- you are safe if you read from the `store` object, you will get the most recent\n  version of the store always.\n- deep references to items in the store may be broken if you modify the store.\n  I'd be interested to hear about cases where this is proving unpleasant. Please\n  feel free to open an issue with a code snippet, even if you think it's\n  something that can't be fixed.\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\n## Concepts\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it:\n\n```js\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n```\n\nAnd referencing it in your component:\n\n```jsx\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nApologies for the long variable name if you're reading this on mobile.\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\nSo far none of this has anything to do with Recollect. But there's some\ninteresting stuff happening here that's worth discussing.\n\n### Keeping references to objects in the store\n\nDo you remember when we did this?\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n```\n\nThat `tasks.slice()` part is very important, but first, a history lesson.\n\nWhen the people that made JavaScript were coming up with array methods, for each\none they would do a shot of tequila and flip a coin to decide whether or not it\nshould mutate the original array. So `splice` mutates an array, `slice` does\nnot, `push` does, `concat` does not.\n\nAnd our friend `sort` _does_ mutate the original array.\n\nBut we don't want to change the order of tasks in the store — some other part of\nthe app might be using them. So we `slice()` the array to create a shallow copy.\n'Shallow' means that the tasks in the resulting array are still a reference to\nthe actual tasks in the store. So if we call `task.done = true` on one of them,\nthe store will update.\n\nOr more accurately, when you do `task.done = true` Recollect will orchestrate\nimmutably updating the store and re-render any React components that had read\nthat task's `done` property during their last render.\n\nSo, it's important that if you return an object from a selector (e.g. a task),\nand you want to set a property on that object later (e.g. mark it as done), then\nyou need to make sure you're returning a reference to an item in the store, not\na copy.\n\nAs long as you don't use `Object.assign()` or spread operators or some deep\nclone function, you'll be fine.\n\n### Always pass the store to selectors\n\nFor the same reason as above, you _must_ pass the store to selectors, and it\n_must_ be the store that was passed to the component as a prop (not the one you\ncan import directly from `react-recollect`). If you try to get data from the\nstore imported from `react-recollect`, you will get an error.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\n(Remember, this _looks_ like it's mutating the store, but it ain't.)\n\nYou would reference this from a component by importing it:\n\n```js\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n```\n\nThen calling it in response to some user action:\n\n```jsx\n<button onClick={markAllTasksAsDone}>Mark all as done</button>\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go nuts.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity?\n\nAnd you didn't even need to install\n`react-recollect-immutable-thunk-saga-helper` :)\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## What sort of stuff can go in the store?\n\nData. Anything JSON serializable, plus `Map`, `Set` and `undefined`.\n\nThings that aren't supported:\n\n- functions (e.g. getters, setters, or other methods)\n- properties defined with `Object.defineProperty()`\n- string properties on arrays, Maps and Sets\n- `RegExp` objects\n- `Date` objects (I'm working on this)\n- `Proxy`, `Uint16Array` etc.\n- linking (e.g. one item in the store that is just a reference to another item\n  in the store)\n\n### Gotchas with Maps and Sets\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYep. Recollect has no effect on state and the updates triggered as a result of\ncalling `this.setState` or changes via `useState`.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n## Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nIssue: https://github.com/davidgilbertson/react-recollect/issues/85\n\nThere are two workarounds:\n\n1. Use React's `useEffect` hook (React 16.8+). See\n   [these tests](./tests/integration/componentDidUpdate.test.tsx)\n2. Pass in `loaded` as a prop from the parent component.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nBut it's generally a good idea to wrap other components in `PureComponent` or\n`React.memo` - especially components that are rendered in an array, like\n`<Todo>`. If you have a hundred todos, and add one to the list, you can skip a\nrender for all the existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nThat's a wrong question.\n\nContext is a way to share data across your components. You don't need this now\nthat you have a global `store` object that you can read from and write to\nanywhere and at any time.\n\n## Can I have multiple stores?\n\nNo, but you don't want multiple stores anyway :)\n\nThere is no performance improvement to be had, so the desire for multiple stores\nis just an organizational preference. And objects already have a mechanism to\norganize their contents: 'properties'.\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` - these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 4 KB. If you were to replace `redux`, `redux-thunk`, and\n`react-redux` with this library, you'd shed a bit over 2 KB. But if you've got a\ndecent sized app the real size reduction comes from getting rid of all your\nreducers.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.0.0-beta.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-m5yGg7O448YeR2pC/g91OQM7nTEfP1yFb55XqIDnAWLaLkq8HdB6WiEqD6eJLNS0VU0D3AT2OxjUBeYGnoBtQg==","shasum":"e604b30be2c368426882cece6666e68a70a219d8","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.0.0-beta.0.tgz","fileCount":33,"unpackedSize":132863,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJebb+ICRA9TVsSAnZWagAAqaoP/iEGg8XG6S+XV8USZGuy\ntEw6aymR4bTBoJ+8XSFq3330kigZwLUPFn8PaWUJ5yeFICMCJFAXyLimDWlf\nGxNU0MN9EEEcbqrO5V9LMSnhUQi+RVuO8q/TIT8ocuvRSQt7WFZeyNL2uhq1\nOnyq3gPPRi7XZPzk/Iq9kPt/qKWC4TDuECQk3OIm6QEAMQB5qRB7/2kVfEJy\niIta8dKjs5SWPhKxvLnHc0/7VXqMG1LRIxJAFLpdaPUOBdD2M/uEJNZPM+lO\nmqS8/d2E9XM9CD/HOOwhvajoQx3QG9pRRTZ6AdGcfNa07UkFEd+3qG7AvQ/X\nBJblDfh1gNLykehopVhfifpJQTkOSB46NXRVDxhALcHKBpUnh0BkZRbV3BQ/\notJ69XOKDRAdnaVJh56kLg56rF9P1CPmHc10ZSuEWg2UaYHenCK2D2RhwYqM\npIUhvpxtxzLxSMQAe6Rjq1ncRCwIp1lWV6rnzqzcjxuEJ9B0WndmNJJR/BuA\nxMntugvrP+t3DGaoe3N4AKDx/z5Iio2UDWYj0sqoQ59LT7aMBX/52KA7MOZH\neGmD3Ax5BPhmKcZ+l6x2hshyD8hY+I4AFMILoYRQrWMfRh1JxUWMia2DTNws\nUk8K5xZg5Scc0hT7NVE0a7G3hKM8v+TCkUdooTNcs40euAA8hfulNfgE+1UM\nJxal\r\n=EVX5\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBWyPcqUk9uhGyi7wbpcz0gdHLz/pm7jgOzw0je0f/tYAiBqJXl3OsFcGlqSg0A5td/QKPWVSATQIS7RB6lrSao4rQ=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.0.0-beta.0_1584250760273_0.48623000122358806"},"_hasShrinkwrap":false},"5.0.0-beta.1":{"name":"react-recollect","version":"5.0.0-beta.1","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"readme":"![Node.js CI](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\nRecollect is a state management library that aims to solve two problems with the\ntraditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed\n\nRecollect solves these problems like so:\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object. No\n   need to worry about accidentally mutating the store; with Recollect that's\n   impossible.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that read\n   that specific property when last rendered is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\nThere is no support for any version of IE, Opera mini, or Android browser 4.4\n(because Recollect uses the `Proxy` object). Check out the latest usage stats\nfor proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\n## The `collect` function\n\nYou can wrap a React component in `collect` to have Recollect take care of it.\nHere's a component that reads from and writes to the store.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\nimport Task from './Task';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <Task key={task.id} task={task} />\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          id: Math.random(),\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide a store object as a prop\n- Collect information about what data the component needs to render (which parts\n  of the store it reads from).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## The `store` object\n\nYou can import, read from, and write to the store in any file. Or, as you saw\nabove, access it as a prop in a component wrapped in `collect`.\n\nYou don't need to 'create' or 'initialize' this store, it's just there, ready\nwhen you are.\n\nYou can treat the `store` object just like you'd treat any JavaScript object.\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nif ('tasks' in store) {\n  // Nice one\n}\n\ndelete store.tasks; // No problem\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore = 'foo'; // NOPE! (Can't reassign a constant)\n```\n\nRecollect is always watching and it knows which components need what data from\nthe store, so it will trigger updates accordingly.\n\n---\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n# Advanced usage\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [The `afterChange` function](#the-afterchange-function)\n  - [The `batch` function](#the-batch-function)\n  - [The `initStore` function](#the-initstore-function)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [Passing a ref to a collected component](#passing-a-ref-to-a-collected-component)\n  - [Peeking into Recollect's innards](#peeking-into-recollects-innards)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [How Recollect works](#how-recollect-works)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Concepts](#concepts)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n## API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n### The `afterChange` function\n\nPass a function to `afterChange` to have it called whenever the store updates.\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\nThe `afterChange` event is an object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g .\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n- `prevStore` — the previous version of the store\n\nThose last two might be interesting if you want to implement time travel, for\nexample:\n\n```js\nimport { afterChange } from 'react-recollect';\n\nconst thePast = [];\nconst theFuture = [];\n\nwindow.TIME_TRAVEL = {\n  back() {\n    if (!thePast.length) return;\n\n    const e = thePast.pop();\n    theFuture.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.prevStore);\n    });\n  },\n  forward() {\n    if (!theFuture.length) return;\n\n    const e = theFuture.pop();\n    thePast.push(e);\n\n    e.renderedComponents.forEach((component) => {\n      component.update(e.store);\n    });\n  },\n};\n\nafterChange((e) => {\n  if (e.renderedComponents.length) thePast.push(e);\n});\n```\n\n### The `batch` function\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan appreciable performance improvement. You can set `__RR__ .debugOn()` to see\nin the console how often your components are being rendered, and why.\n\n### The `initStore` function\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in. If you don't pass anything, it will empty the store.\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n#### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so right\nbefore rendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n#### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n### Passing a ref to a collected component\n\nRefs just work, as long as you don't use the reserved name \"ref\" (React strips\nthis out). You can use something like `inputRef` instead. For an example, see\n[this test](./tests/integration/forwardRefFc.test.tsx)\n\n### Peeking into Recollect's innards\n\nSome neat things are exposed on `window.__RR__` for tinkering in the console.\n\n- Use `__RR__.debugOn()` to turn on debugging. Note that this can have a\n  negative impact on performance if you're reading a _lot_ of data.\n- Type `__RR__.debugOff()` and see what happens\n- `__RR__.internals` returns all sorts of interesting things. Including a live\n  reference to the store. For example, typing\n  `__RR__.internals.store.tasks[1].done = true` in the console would update the\n  store, and Recollect would instruct React to re-render the appropriate\n  components.\n\n## Usage with TypeScript\n\n### Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n### Using collect\n\nComponents wrapped in `collect` must define `store` in `props` - use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# How Recollect works\n\n> This section is for the curious, you don't need to know any of this to use\n> Recollect.\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object, but it isn't.\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\n## Concepts\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported:\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself - a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYep. Recollect has no effect on state and the updates triggered as a result of\ncalling `this.setState` or changes via `useState`.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nIssue: https://github.com/davidgilbertson/react-recollect/issues/85\n\nThere are two workarounds:\n\n1. Use React's `useEffect` hook (React 16.8+). See\n   [these tests](./tests/integration/componentDidUpdate.test.tsx)\n2. Pass in `loaded` as a prop from the parent component.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough - especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` - these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 4 KB. If you were to replace `redux`, `redux-thunk`, and\n`react-redux` with this library, you'd shed a bit over 2 KB. But if you've got a\ndecent sized app the real size reduction comes from getting rid of all your\nreducers.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"11b643c883474655fe149bc8a0ee522960568d1c","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.0.0-beta.1","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-kfdGg3oh/6rdVgl27l75DVIREIE4uRLhkVtwEDd68qZO/5t7ZHU93AvoSCw7zTu/Jmx3/o8QvSOipR6r0bEByg==","shasum":"b45b17f7dd40998a35bb64dc5cc592275f282825","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.0.0-beta.1.tgz","fileCount":33,"unpackedSize":130349,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJebcXXCRA9TVsSAnZWagAA+IoP/RlZc/sUNL7U23P84YUN\n5S+oQSGO7G+UywHMJOAxc409sxQq7FYOgXlf5Hj7VALlDlAtwvRznbtA7esi\nv0VlZoU+v3SJOqAzRgTln6mzxrM05wWH1+G/Wcu/sTt9+kv1VfOrg+FZFuc/\noBHizUeDD8IZDGzdzINrtH/ZnJffKL88CY50di0cklc1sfuK13YKD7UZYsrD\n9/0ozX0jgMd1/W1hhOrhluU0HnhKX9cHAyUk2ccK6W49g/XyxGdkKsB2CUr0\ncX/8p9NeLOG1rvRniqHMigSxkgr640WYmw6IcUg37TZ8iZafBi5jHhBOIeYZ\nd+mn8kz4ttjVCIb+7FZvO9Ahpz/ky6udfUBQgnwqc7Sp5DqE/zTT4+4wZvQ5\n7Qxfgd6c+M6N+P9ORfGnOHov+d0u8vH5PV/w07Md9Jq3WOAUt7eU3/XQlDvn\ndEYooSYhQ0GIaZZIJdOca5CkNYigvFmFuOB/OYprSt08SOXpbJxcWWsCtsSD\n9L3g+QiWEGJt0TpkfHb1N7/2Lg67tZoNLaCkkhrXn3CK+k4bCPJ9ExS93Btm\n/zm8u5GlJ7NlzFiRYMLUx0kyewFXTyxONPR6U4XUUPWQEQw3OqaRnP9i6W2y\nXzfWpwbxztVXF7xf/WfRovyqoaVkEYkvlUTQ0IqvJeWYN2awdZOUQlAHUWAs\nFwtH\r\n=OzcO\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIA2Hpl62OhtSuG4FPK6Hwt3wpV3XGs0bE+7azUJEevdWAiB0ooFUmhD4eNKIGPaRidAnJCmUS1UJ9sVk1kCJA+vPjg=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.0.0-beta.1_1584252375123_0.6847104264896924"},"_hasShrinkwrap":false},"5.0.0":{"name":"react-recollect","version":"5.0.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepare":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/react":"^9.4.0","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.3","@types/node":"^13.7.6","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.12.1","@types/testing-library__jest-dom":"^5.0.1","@types/testing-library__react":"^9.1.2","@typescript-eslint/eslint-plugin":"^2.21.0","@typescript-eslint/parser":"^2.21.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.0.1","eslint-config-airbnb-typescript":"^7.0.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.18.3","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","jest-dom":"^2.1.1","prettier":"^1.19.1","react":"^16.12.0","react-dom":"^16.12.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.2","webpack":"^4.41.6","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"8e90aabe6701f5ddc905cd3ae3013c3ece32a5a8","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.0.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-StZzATA4JEQZniUag/QAwAOe3b96OO0rjyFMnDy4EiVh7XTD8ICaGVJe8rvPdUcdLOJcFtSEtEvXanaIJZ/SoQ==","shasum":"1c7caf0ab76934d4ce374bdc9d0445eacbbeb220","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.0.0.tgz","fileCount":33,"unpackedSize":130342,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJebcyrCRA9TVsSAnZWagAA2f8P/3RPd6LauCzfVJCDgN8y\nI1GQI+Qio/N6wJiqvlOp7mptJrA0Q3MluyxjYhyF8U63i4C8B4lX7Oi8UgkF\ns9QY5vxWEFh7h4qLuVdIk14pWuUqK/94lzhMy+myMOgASNamCRA+1v99XkBa\nwpLGaSyNbFeDuqOa3lm6B+Zf/vep4poUvFjSyeLT/MHAVXT5sI1jho7CjdOQ\n6QQN0ccnJA+eFuIZZ8IVztzbTj468uIfLH6BcJDmFYGBwMuj5nw80hqVRo0m\nmA3zII3BSUGcVJ1WkfFmAeFPM+KFXSahi0jxk71UqHcwkHWHAH9X7vtyLpa9\nysevwpmXtwgIPu7q12f76+jk33EEiJ0Ikg6vR41BX28zc8WFpdn28UN5hoeu\nUrJ/fqygCprxH0lLq8BqRsnTTGL7XkbIFAfYPvzykvT4D04CN8rkN7zR2Mdz\n7Yelfzl/djP187km0LstSXKjQ2fX3e2yQOHGOB+/gt/QrfFVcFVbTsMY2mqZ\nqFEj9cSu88EySZPRYkfK/JPQE+pm2BVmbj/PnIMt7Y2/mG+1cJjmDY9Sf4Jr\nwodo+dIQCpanJJFVxs4FVGegQ9bWtve//87fW/bblQx5zzxV/UqHsBxIv946\nZYkySWGRSop0gp1lPlg6tC8AEozjo2IWQGh838pbaW2PL0YvW/gGRDMRhsOp\nii9x\r\n=+ObJ\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQDwzUSo6p9FQjzPFTR+A+/qR50iwlryIP13W9uJGkfSvAIgIN9MycHBll9r9m3J2LeNRyJXT0gO9cdtpUgaqoceK2g="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.0.0_1584254123065_0.11102368371527205"},"_hasShrinkwrap":false},"5.1.0":{"name":"react-recollect","version":"5.1.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.2","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.14.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^1.19.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"110a8e5ee745c3d780d326c557367f5452a34e2d","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-aXl3rorF5CS2ZRVhM/4UkIx6eRr0DiLEpYEj9KnU2f1Gdnd9QDM+LzUct4S4TmpuYkJzG0j4tnvSS4lcXXqyKQ==","shasum":"b3f9408ba40f6c9026e69f907001f9137f6614ff","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.0.tgz","fileCount":29,"unpackedSize":139241,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJecyQ6CRA9TVsSAnZWagAA4KoP/2q0L8tVqLKh61TIVbEj\nHIGiTUfp2cTUjQnpDRr7PRrkXgmgdOKstzkNmgQH/zqnIX8i1zy3VJLH+Un5\nAVxFYxfnF7zaghczvN9Gx0e8yAxs+GLo/vMZAAUa4GiI2/wTBsuwg1nVVjCM\nQpmgHrjtKnYZliNu+Rm/9Zh41kEUkABjDEP566/vtKPGh5+vH021V5EXmeNO\nhfnM04df4Yy6nXdyC0+boINo2UZdtNQVVGnkmqpBqgvruTkkM/dnSoYFkRLh\nUPVsk5GyS/CaRDnm6x8R90mik3wgG2QknqxviCFy1kl6yjI2ybwDLa01UaYq\noinQR+web/xBN/HJsg5E+Ft06UOApSbVibeC+V5GAZwVu8rlL1ETuZcMHSUT\na2M2o0p1eqwqQKabY+oVZVbO+1XF++CwQ94LD7ZkLzgPptEUAS3dFNkviQxh\nXRC0XsIALMnVdm/fUwSFyiatay6zLk22F6RZ7FnDN0zgxZwcIkY9uEz7G4VL\nGcZnQ9I1Low0bz1XjP19hKxUUpNDnQ8VaI4FQw1Cn29SKPD3FKeBpeIfaI7/\nO+DrNKSJQXh0ElT/sFk+ddoEit3+n7DYDlTdNxEeMLO1CZ7r8t/7JsWOck7r\nXCPSvwYj2NeB3GyCnx0omqQUE6d14ERW/27BwBv8LZxafmnv7QTX8lwQPhxs\nyGuv\r\n=QMGa\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIEZXc8LSAXmlxZ/N4suuGt/oP0O4gUCxclhj09bVMMCPAiEAjZDzmYOV2Yl7kGfXUetgVzJUzhKb2SeK/Zu76dlMsQQ="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.0_1584604217655_0.4868887402231803"},"_hasShrinkwrap":false},"5.1.1":{"name":"react-recollect","version":"5.1.1","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.2","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.14.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^1.19.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"928b194fbd203ff15d90f4c314fde18e04e869f2","readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs.\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that uses\n   that property is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile. Here's an abundance of examples to break you out of that immutability\nmindset...\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nstore.site.title += '!'; // Exciting!\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n(Internally, none of that mutated the store contents.)\n\nThe `collect` function wraps a React component, allowing Recollect to take care\nof it. This will provide the store as a prop, and update the component when it\nneeds updating.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know that something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop\n- Collect information about the data the component needs to render (which\n  properties in the store it read from while rendering).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied.\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. Remember that you can also\nuse `Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](./tests/integration/useProps.test.tsx) for more usage\nexamples.\n\n## `window.__RR__`\n\n`window.__RR__` is there to assist in troubleshooting/development (they're the\nsame thing, right?). It has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some useful things, but should NOT be considered part of\n  the Recollect API. Do not rely on this in production code.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store,\nre-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported:\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`), then any component that read that _target_ will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](./tests/integration/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.1","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-UIFlXT4hgk2beBFToc/TxW3HEFYmvMfSzIVDFSXs33pTpro81yAa1sThp1LVJAmOyUBSZbmjAb+Ba3zUt6McPA==","shasum":"ba5ae10dcc2761c27d67b789e5bc64ca0026caf8","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.1.tgz","fileCount":29,"unpackedSize":139740,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJec/RBCRA9TVsSAnZWagAAvjcP/1125ENxpU8td1A3Yz2q\nN4iyag7jmJ4OWri9gMGPE8xxDn8QDASJcDZcJUkLRqRWF/SSc5ucbR9aAnDg\nGk0iL7ggZyxtiwVdJsMl39aWdDraXl5KeMQb2Nz2m5iZGJjGczni8XECzXvC\n2sfJhhRyk0A0N2GES7jr7etCPr2KbFJatp2Q4YfNyVWVWKLaF5Bk3jV9Wxq3\nz8pk111Vd0M9lsbf1R4JvZqagrtHl5TbQdZUfL4aBvaRYDxr355zLi1bGmnp\n0NNhvg0v8cYwzZ/SphlYIvl1G7YY6MvcPzm0Yvnk87ozI9SoFwySYs2oqt+Q\n8nzc7lh26VVeZYDUD+NgOnzAEXQY/Vvfpd7K+oz1ShwuNkp2YcwPwgrQQpdx\nwIietAKLZoLEZOF3E3mGPgfET9F0bT3ZoTj9dLbGRShm/ow7KR/1L5qlJXD3\nFrUQIZD//nXUiNyzrOSWDZmTS08c7kVeMD1J1tKKXw6T9Ts8x47r/oMg1Rap\nuGrVGLHInjgyNzMeQJtxJDKV4F9hG92MAIRvwT1vTbYRx1dbPJd+FdqtfYDs\nP2v857bo6Xmzwhi5ZwiMJOP5ItVaKwtR4P3mR6MoCcnm/ydGw0jEsuDzkeJU\n74P3OhHbZ1MQsnQHKpgcVtOV3XUslrVGEJTHbb4nz5SxzQHpRj6uR6sWg1Um\njW10\r\n=3Mov\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIE91EVNZeVD7HzCvfejh45nAFdkcBfdeqGM7MhW6NVXSAiEAp9ofGO/YXRwmG5q4v4oKFYIFhtbXrfSJ0RXZrWM+gAA="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.1_1584657471898_0.2102269173410265"},"_hasShrinkwrap":false},"5.1.2":{"name":"react-recollect","version":"5.1.2","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.2","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.14.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^1.19.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"a3a78e18a0af891d16af3515405ea42bb7c04bd0","readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability is complicated and prone to bugs.\n2. Components can be re-rendered as a result of a store change, even if they\n   don't use the data that changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it was a plain JavaScript object.\n2. Recollect records all access to the store during the render cycle of each\n   component. When a property in your store changes, any component that uses\n   that property is re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[Code Sandbox](https://codesandbox.io/s/lxy1mz200l).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile. Here's an abundance of examples to break you out of that immutability\nmindset...\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nstore.site.title += '!'; // Exciting!\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n(Internally, none of that mutated the store contents.)\n\nThe `collect` function wraps a React component, allowing Recollect to take care\nof it. This will provide the store as a prop, and update the component when it\nneeds updating.\n\n```jsx\nimport React from 'react';\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you just finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on ...\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\nIn addition to [`connect`](#the-collect-function) and\n[`store`](#the-store-object) above, Recollect has three more functions.\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know that something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop\n- Collect information about the data the component needs to render (which\n  properties in the store it read from while rendering).\n- When any of that data changes, Recollect will instruct React to re-render the\n  component.\n\nInternally, Recollect maintains a list of 'listeners'. The above component would\nbe listed as listening to the `'store.tasks'` prop, and be re-rendered with any\nchange to that array.\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied.\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. Remember that you can also\nuse `Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](./tests/integration/useProps.test.tsx) for more usage\nexamples.\n\n## `window.__RR__`\n\n`window.__RR__` is there to assist in troubleshooting/development (they're the\nsame thing, right?). It has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some useful things, but should NOT be considered part of\n  the Recollect API. Do not rely on this in production code.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store,\nre-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported:\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`), then any component that read that _target_ will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](./tests/integration/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.2","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-PW3yln7Obff9pLl66OSxFxtSO3PU1kbTCbe/yQVxW//0DN7JOzBP7sTRbJSfzdBe1ZGpiSDMzmNrvOzvrQDlZw==","shasum":"ea4c9ee302f9f302255d402533dae437d4b11b33","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.2.tgz","fileCount":29,"unpackedSize":139641,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedC4iCRA9TVsSAnZWagAA6NMP+wUT4a5HUTRLF0x+oKlR\noIbXxAo4WHEtUT19xmeoW4VDhRQVgsI0ji/j86zGvusVq8NGIhFaDY412Wat\nAFC3PcN4raTvp9HcCDunOFoGMStFAlZWbRwQdzGFodf4323E8xPlV83d98l6\nWMhmwKBAQPfymfAa2BT/tP/fHgg8ry7dmXAk+FjaVDNRyvFjtbq1rj6ON/P8\nwDLWwat37yXMk0POFY5uqUWwLTOWrxwG0fcz5oBNcN81qOL0BX1DgqFeBLgi\nDL+BsBYzqtXGnHSS/Ul8glMX+1M9bVWCbA9+EOTlCTL16i3AbBK+DpV40dQU\nBSXaLQaVLrn43c2S5FH/p0sy1xNCW6igmRRitaXXNrrPxUFWHOJQIsSnC7Jl\n/I506dA/3WJWjDrSph6l3bOYHClM+Fo7hL4x+IaDEBM4rQ+shlXdl1RvWDm8\nyF+llXBY/u1zlnBIkEmt8AoeicxHAFGI6RuhrIf+CZ4r4J0Qm+mkHbVRc+AL\nVIQePxgX4nnniCRwWW4S1uv1hTIT/N1Xlt9L1rDOFt4Kb0M4WDfgQSoyQ9Rp\nWRtwGKMvk87LHQp/Nub9bt/ZK1TCsStK6WEyxs65dYU7JY4iJZLLCL5cWUPN\nIdxPgVtEcd8YKQZAqSMd+H6yq+iFUVGsh2WcefN8sTXwkRAfRTAy1h6Pvu1P\nakJO\r\n=255Y\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIEauxME+ZTC+sJ/cuzQjl4IOluMc3eTko8hD/QDFhQxsAiAo+q5DYGqhaW1p3jDa0/qN6wMTkwsDUrFbCQwESEvRng=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.2_1584672289517_0.8270495217827933"},"_hasShrinkwrap":false},"5.1.3":{"name":"react-recollect","version":"5.1.3","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.2","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.14.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^1.19.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"f8eae2b4acaa2f871c8e135a64ccc555fca36702","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.3","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-7xaQBenRZ57j+HZ0/4tlquLwXXHK9ukCSWwjy+f6CwFauEhDkPQWMxIWDtz+C0j1NFXM/ntwiYfYihHdE7RvYg==","shasum":"452b53bbb936e433cbddf8ef579def0c8b1d8d87","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.3.tgz","fileCount":29,"unpackedSize":143410,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedJq9CRA9TVsSAnZWagAAxD8P/1bz7v3jSiafwjo8rm5Q\nUenhP8Lz7jmbP+RVysR54tkN+icWc5v5gfEOTvwIBoS1rX6z/UFiWEmsOtFM\n8mcCw38EHqE8My0immiMvYIj/0ON4GtZ4AsH6jUvUN9J3zdWWiJZWvvbXkAS\nizgY3O5fZvH45L2pY8OEv9Y5bXNqzlL+KywQzxFwaPbu83RbBhtBT+MHJKXd\nuM7KOdj3NSumKfppj+NJ71zSrcb25H8ini8J82fZh8HFDbKgIFNTqyF8c8O9\nZxShFqn0DwSXPxlqDGrDHvBdV0tClg73FmmnfpPRrBXG9EcMWpFbWwPQ0kPI\nhIE8h+OMYN7ncBoC0LgGEOq8kBEuWE3FJ6vYDsgWz2kLt5Kx5IWS9DLRT2vR\n4NYgzLqCX0MlNVZa3e/Q4r9goxZu1PrZgOQtOCsU6SZwvQt6DJR3diEelSpC\ny5FGI15qkA8dhjLTBLMRWWdrvp1OomhQAgwepxSjuPovtcDWBFmdeXfRhZwc\nP7HZ96THs9GfLjTwhSYCdmpAfdK1C/WytXVGY7hemYIyxFIw4/sT535a4Eku\n0OFRHlHlXJhcVJvDnRdMvF3ZN2AOBp1O9fq3exqXq+9ELFKaVJ9JS88tNxDs\nyY4RAIuIlEbNLBBmjv1DFv1Ehs2jRRH/KVdqfXxGDntlDX7kTvb6698XppiJ\nzTul\r\n=KXTf\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIAmxG6LE5dkAdYPbKTcgFTxrYF/Cf+wraBstoME1UzC/AiEAjW8KM4cOIxhLLjgwwAcjL7mMpp6cO5uUdRviv7zoPEU="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.3_1584700092931_0.12857314195210368"},"_hasShrinkwrap":false},"5.1.4":{"name":"react-recollect","version":"5.1.4","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"dist/index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch --mode=\"development\"","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.2","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^6.14.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.0","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^1.19.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.1","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"gitHead":"b3ef7808179774fe999aa627b2aec724a14556a4","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.4","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-2ACgNsIHyky54ZzWm/KkVz/68GQz4b/4au4KHUShgL/wLHKmgwwTKkbZlxDRDT4OaXn3fiCWcM0grQ1RmRJiuA==","shasum":"7957601adc16df5467ef98a95b9f76a970ef197e","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.4.tgz","fileCount":31,"unpackedSize":144587,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJedbl4CRA9TVsSAnZWagAADV4P/0Z8tfnMO3h4sTEpdr+x\nK1YaSnRDQuK64ir6dv2zPxAN2MN8B7J2Tq6D3FqErZ2IHzbydbGsFT3fQwkn\nVM14SIk7UPKdJGYaF5iY7tjDL7HNXtLh/0FaXLlGfWkuu+aOMcU+C4injTVd\nvhjO/A96zUotxt8Hsc+vglxqOXhXJ208rlE/1v0nozmfADPN1oL46S9Gi/GS\nKsJwXuasv4VcxBBRzoEWcrcz5Zpdn9/GcS7jEI7EjkaIdIpoRpqY/7Fbxt2c\nuAdygn3kEp3lqnne4Bm7Q9YNOKbznpgP0cgDUzx0uGA6MTZ+BIYrsMCUt0Dl\npEoaRzk+WxewXl6uSlwIEAVh/OmQMHpTOdfpObTvaF23AnpXjFp0t5W9x9kI\nmVrtVaPmSWFgEkJPCRjBWlQxYDf0zyUVqeqzxSKolaZ9009P4ChM8Y2mp3M2\nECeKca6Okjcjv/d4cfuOmFbdsVMd2iXPfKKVsMVUuYj/OPGM6hkRotQZUOcw\n1KLLkVDe05+0QU7SGZZ8lWmHMbX306PrZrkFlr6cqvjv+hVB47F30p/WVAH0\nb14BnpzOvXBFPaw7PF2hwBQkFpwKL/SRz6+QJIqW4FYmLSd8rO2SZpJZC85e\ncSJeOxkemFhAdth7mAq98ZuhqOORG0grYnw1hvfgWHcpdaGUWvqmeKvTFMEP\nSaaP\r\n=BL0E\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIB4UOnRfPB6AlFXlOJewl9MVRFVaRk9KcLXwgbq1hxSsAiBBbTEgjepPVeBcSzFwtgyMjf5OEbL5LL7EqNPlbJoD3g=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.4_1584773495954_0.6866669934769807"},"_hasShrinkwrap":false},"5.1.5":{"name":"react-recollect","version":"5.1.5","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^2.0.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","terser-webpack-plugin":"^2.3.5","ts-jest":"^25.2.1","ts-loader":"^6.2.2","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `window.__RR__`\n\n`window.__RR__` is there to assist in troubleshooting/development (they're the\nsame thing, right?). It has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some useful things, but should NOT be considered part of\n  the Recollect API. Do not rely on this in production code.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store,\nre-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"d06951c55590cb88d0a7e5729bc9aa5ef0c4629f","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.5","_nodeVersion":"13.11.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-hV/+kzAT45nMzXclLMZ9pnHU8qEfBAA+LzcofUK/EKOfJD8ZGt652CyAqWB0Pby7dhNc+bCLgQxJ+B/Ix3mvQw==","shasum":"97faed61f45d9dfa5e06f1e08dd8013db1213fc4","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.5.tgz","fileCount":33,"unpackedSize":299278,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeenvFCRA9TVsSAnZWagAAA+IQAKKHvZEQZtG6XmBVC3kt\ncDSffoZWwtCET6cf9lLdfxdyROOdwLgksJNnz7UtD2z2E3l9ItRXdNo3N8p/\nApf9wX6n+F/JyUmxwYbOsH1/M5OZpxA2rq3rskGCOi+g84pSX/S6Mfiwx04v\neDr0exjxFf8dfgD+uQbvZvUDp9OQZUfcLjVvPdpN2rAgMx59jRtu0zAyHZ7I\nfjUZwZQ5rOhpkFHmEtO6i+w8vziE9TImSWW4Hs+kpCPxPaH/PKjAEg7leo61\nqQRqcs891zwv2jx0FTuq+SpXaON3OSxSd79WVBYMtnCEwsCHiSetWiNuaCq+\nplhkJ0vfFd3TUOyfox04ARv4nNvuEpxsaBqtL/50Ox4pNRYBLIZ3CxRdP0AE\nH2v38D1P53li/k2f1wq5OdJPOzFFyknidg+mLXzkdHyGLzVAq0kHa891CCzn\nIapkCXDSV/wr1k839Eg20cEaRDVTmwEClwor/0aM2ySzwm+4CDhLZf0bQeXj\nMmshRfkLtHd6/tIoTWvzJhBEaqWzXL/6lQg8jHJVGFhO2RvcO5XWqcaxtF28\nmKtBfk8rc1bKSF5PRB4RaHfu/3gHhhf31VBvapR6wBquBb4YsFRCgyoKnmgg\nHANIh2YPf1VSpNeq32BqSD7S9EOvZQeQbT2/Jgf3qKO89E1zNgZmTqKqIani\n5kNz\r\n=URT0\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCICnS8bDR6PeHUW3dt4NaVTlFipeCeGIxpVSfVT4SWgTJAiEAwlKyK6E13fGfgZMhnjYuc3jimvhOVAr0xBeyISbLWEQ="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.5_1585085380651_0.9778758652087189"},"_hasShrinkwrap":false},"5.1.6-0":{"name":"react-recollect","version":"5.1.6-0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^2.0.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","terser-webpack-plugin":"^2.3.5","ts-jest":"^25.2.1","ts-loader":"^6.2.2","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `window.__RR__`\n\n`window.__RR__` is there to assist in troubleshooting/development (they're the\nsame thing, right?). It has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some useful things, but should NOT be considered part of\n  the Recollect API. Do not rely on this in production code.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store,\nre-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"ed31e8833e5038da43d8da9b9791ca36763d74ee","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.6-0","_nodeVersion":"13.11.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-x6y6r/BXX8kA/vlX/NWQ7qd1SGHDW7V8BFLDxz4duIRWUJANMen41Z38wDLL8Yj8zUzHrCn/ge74di+GIrPX6g==","shasum":"51b0bb7723ad768b947c567b37074c096a7c1443","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.6-0.tgz","fileCount":33,"unpackedSize":299174,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeen44CRA9TVsSAnZWagAA0CcP/0eSpvqZUIwycAxnUro2\nO843a3Hrx2W75iqjogaD5Jzd17t1wkP8Il4drjVq/9b3MA4Ih9NEHByj24Mx\nVcXDiLuOuHieAKsd+6TNSjsi8pRY/VPj5FAAYyp6gXpBi7fQSG0OR0OAb1U8\ni7AV06RMVhE5WUAgcVoC8yArerDHAzUsKggAOaGNq0Lh2rGSOyZrb8EkNBI4\nHqpWvGrRh0LeFdo6A4unUHburee8YJYUObBYm0oA0rDYmsGYoRMF4J4i7hj6\nEzG3cMHnP4DcVs53ob6LZXJpCk9xHcGIebqcfMo36w+JVIO5L2OGgtXYHI45\ni+PRZmsdmCS+Ib8KqUXfU79Zwn5T8MBt+kPOXENNv6ZVA0q2uwxht44xQG8T\ncznAQOUVDkybR9ftPYaZrBE93fpD5NkBLFq/15iJhbGtAd5bQpXb7jacvA42\nfaiy/3y7NzjMiLe5Mb+HwcyOFI7Y1U/TqtbJMXb/wgyjfdHEnx+fuNfCNHtG\nc7OIwNKFq7twMlNonfSTEW8+sS3LFWkAr55LZNsRAFP+kSsCp/noOfTeXmIg\ncq6AzCDU/wvw4St7j7q84Z/L6LbRSBoMJb7WNpAeAkLePrOmJ9twCvcMGQkb\n3Ya3Vnlg4f2QqZhl93ouDXvdWKYqY1VFlDdqmdQ9J+aUcuI9+mMSIqaizB9S\n9sf4\r\n=apad\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIF7DYwwgarwsWnP5py0UaAbzCj43tDa1FNMf+nFjMsZNAiBWUcWKzdlI2nxjZK0WcFDporIRXuSMameUj4ZFJnvhkw=="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.6-0_1585086007613_0.8581171453878182"},"_hasShrinkwrap":false},"5.1.6":{"name":"react-recollect","version":"5.1.6","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest --watchAll","test:ci":"npm run nodeTest && jest --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"hoist-non-react-statics":"^3.3.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^2.0.1","prop-types":"^15.7.2","react":"^16.13.0","react-dom":"^16.13.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","terser-webpack-plugin":"^2.3.5","ts-jest":"^25.2.1","ts-loader":"^6.2.2","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `window.__RR__`\n\n`window.__RR__` is there to assist in troubleshooting/development (they're the\nsame thing, right?). It has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some useful things, but should NOT be considered part of\n  the Recollect API. Do not rely on this in production code.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store,\nre-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"10b400e4fa0064355f9d0bbe9915dd4c911f2441","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.1.6","_nodeVersion":"13.11.0","_npmVersion":"6.13.7","dist":{"integrity":"sha512-xlvN9ueU9Q5/EFqUY/5J2Eh1XGmC4Bktr4NzffZkMoUX7syZnPkz9wQOTyKh82T6I3YSAvr2Mgf93EHxMQ+iSg==","shasum":"f160a58435a0ce9f55b4c9d01bce0191138b6a81","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.1.6.tgz","fileCount":33,"unpackedSize":299172,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeen+OCRA9TVsSAnZWagAA38wP/REoswxCUdeBrQJCX28X\nJhgbI2UugKfOVXYcoCADWc7z4zNW9m8PYK8WfDhPzHYIp/OjoDObG1HcoZet\nccZS/Lm5Mq8tPDmH67nniF2MedPXWQ+GreyJZneERwz6A2rI9DMwnY6A3wfm\nsZv6PMDprZ0yVEQkPehcT7JyhuT5u8TezBlWFSvvYY2LdE6g00L9zc82YZDH\n1lK57tpfG8c4X46A8kA6W/8N7TYaRZitr2p5/ZzuiZfG9NkTl/8o8v7J/xfZ\nI+k6xW+28yLO7taPFEgOnN+vIUJTzP6qaRqRoxuutzJNMRR0412LK/igVhfQ\nSixlByBkaSxtz19lRtBrtkcGId6LYsxOohPL6rokeRbr68QkFKhoJloAqcbb\nQ0woOL0J+WWoOMz4CEZd75V7gNLPcBqmtR6Jlg7wsQKpOL8AgrlFr2IYntWC\nvcNpdscPVIIeDS/UEiZZHW66BILK58XlrULdtatmTt5fKwiUItr6NV+6X2Hq\nfXK9jJFxOXBgvRfE+UPd3WPDe9mk/gLvOAi2kN5tg28f7naKDUqPb728Eq8z\nXplm2cjuJZZf/qFBKYQKzpwZiAUpRMMDq5xGbug72ksNnflr6oL1XyaPP+Ej\n6GFWngd3XqPDDJOmjnnx0lGPmA7THhRVCSWNcajKK1kwYeNiibHikz8OWrJ5\nrQ7b\r\n=HIxF\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCxIbyp1KJZf/w1iTEKZUcOKCE3roT2q00YLuSxgkPNxwIgaaA2rmRJfrEYBsInwHhlIdV7PBQPp1vxmAoP7P9OMpI="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.1.6_1585086349880_0.9525860022506913"},"_hasShrinkwrap":false},"5.2.0-0":{"name":"react-recollect","version":"5.2.0-0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.js","types":"dist/src/index.d.ts","scripts":{"build":"webpack","readme:toc":"doctoc README.md","build:watch":"webpack --watch","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","test":"npm run nodeTest && jest ./test --watchAll","test:ci":"npm run nodeTest && jest ./test --ci","prepublishOnly":"npm run checkall","preversion":"npm run checkall"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.2","prop-types":"^15.7.2"},"devDependencies":{"@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","terser-webpack-plugin":"^2.3.5","ts-jest":"^25.2.1","ts-loader":"^6.2.2","typescript":"^3.8.3","webpack":"^4.42.0","webpack-cli":"^3.3.11"},"peerDependencies":{"react":">=15.3"},"readme":"![Size](https://img.shields.io/bundlephobia/minzip/react-recollect?label=Size)\n![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit your store in the console.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\nIn the [tests](./tests) directory you'll find:\n\n- Unit tests that test the behaviour of the store directly\n- Integration tests that simulate a user interacting with React components that\n  use `store` and `collect` — these might be interesting to you if you want to\n  see examples of `store`/`collect` being used.\n\n## How big is it?\n\nIt's about 5 KB. If you were to replace `redux` and `react-redux` with this\nlibrary, you'd save a bit over 1 KB. But if you've got a decent sized app, the\nreal size reduction comes from getting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"9a1117e64ba95bfa625c8b43de0b1abf851a8e63","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.0-0","_nodeVersion":"12.14.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-wA75Js7y5+Gvc5JGJQaYX4SFfGEB2jIw6sejsWUfz6vILqLFXVVeBVmw7HOczOQ96JerkAoqVEKeowTTPMjVjQ==","shasum":"bf0ba110ddff11f1c9d20b7b25fcf9e77c7a4dab","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.0-0.tgz","fileCount":35,"unpackedSize":390190,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJefXr4CRA9TVsSAnZWagAAwpMP/jfDZgHQwWdb88ibyGwO\nKA1CSJro18Y9yFAySAGQnZCKD0vAO+XwrTAlNHguCF+7RS4DLdszT/6iQDd+\nRVJTYvOJ6e73NUW/ypde5kKvO2Japvho7Vj3XGcAxiZNx2SMtcY8XUSiaAOv\nZtZPjB/sA/o//HFqoAM2VxawrXrzPa2SfITiyotT3Zph8AgzdcMIebfEBszc\nrkfKjADfLesYZ7VuFRWSGOVuqlMyovSkCHwzGEcMD4Y8xZumXsN/Z+Ldmz1p\nqPwC0DzPUbKxgsXUQd6Sqt9dGxnCr4Y30ZT995y03gWTQIpFvxslN0FgSYsD\nR6XQNoj439aAKMKayQjz6UuzzV4oNrT3Xf6/huSiYDKTTNeOHQEd0FUBVtZt\nomZ4/vOBb3KhgRDKU8NzTbZtZpJiAN2aUjGttqy7DDjSnejdWtvjhk5T4dJF\nw8JgaRn6+aM3UrKqTuLKmHOmdpfegsX/XjDQjlsvSsEzG17cBHGwOkrCxt7Z\nohP74+dSEBUb4bPfmwfDm5NAnKg4TRoMYOkyZeSSqCtCa/IWPpg4eWXsWlmS\nda548fRBTsTvJ4h2YbxJx3fwA02BEBZEA6AwxRjWxtp0qK8jkladHc4+QChT\n/qKD1jiRgOZ+Q/1F3Ij5bePzkFDBMqckrJKygE0yRw07i14HcnJ2Tc+LEMiH\noT2f\r\n=+htL\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQDalLRzeOT9tWfJntw/Eogr4MxdFWihzP8hN7XheF6HOwIhAPDbgdIACGZ0yN9La8NxTkum7+L+bu3jwiMenfi+gqDT"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.0-0_1585281784207_0.8657804638049451"},"_hasShrinkwrap":false},"5.2.0-1":{"name":"react-recollect","version":"5.2.0-1","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","build:watch:dev":"rollup -c --watch --configUse=dev","build:watch:tests":"rollup -c --watch --configUse=tests","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run checkall","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run checkall"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.2","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react":">=15.3"},"gitHead":"75bbca6e846b109636a98513e54a6c6726569dc8","readme":"![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n- [Loading with a script tag](#loading-with-a-script-tag)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit your store in the console.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Loading with a script tag\n\nYou can load Recollect via a `<script>` tag, if you like.\n\n```html\n<script src=\"https://unpkg.com/react-recollect\"></script>\n```\n\nYou should reference an exact version in the URL so that it can be cached. To\nget the URL with the version, [click here](https://unpkg.com/react-recollect)\nand copy that URL.\n\n[/demo/public/browser.html](./demo/public/browser.html) is a working example\nwith React and Babel.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\n- There's 100+ integration/unit tests in the [tests](./tests) directory.\n- There's a `/demo` directory with a Create React App site using Recollect. This\n  has a Cypress test suite.\n- There's [/demo/public/browser.html](./demo/public/browser.html) for manual\n  testing of the UMD build of Recollect.\n\n## How big is it?\n\n3—5 KB, depending on what else you've got installed. If you're coming from Redux\nland, you'll save about 1 KB in library size, but the big savings come from\ngetting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.0-1","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-SG7DG0FlCP+pcOUIyfZSxJ2Fct66JtwSnJjsE+ETuJfglu+SHnqs31WaY+1Y+mjCfk2GHe0uNsHo+eQsu9Maew==","shasum":"2dad63a50f6be869432354ddeac11355dded4360","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.0-1.tgz","fileCount":46,"unpackedSize":292236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJegUBGCRA9TVsSAnZWagAATCYP/Ay16Q6r9sSQyvKcvVjV\nSHcUWQ6tJ8n1kmKVi30GdRVrCfne8wlmCl97pJ5qYE+q64UPQ1LB4msZeb8T\np9JmjQu+dIf4w/L+P1uCyrMt36pxnp0/pFj/RdBPDVHSE6KMBgQK4loU7ahH\nhmeyqezhhV5YufBeZhfQ1lGjCHDYKpTbon29FhjspYyexNGNFraGXw8whIFI\n74EaoqjierfbdP/gRjc1HGvgJK27i0RB/SWivuLYC2OCPf0OXKExfqq0WuOi\n2QVEC9mMTSPOi67R6qwKeMMhXYB6hUP0/2HD/vy2Q6shy8l+8Tvr9BeoboxQ\n0ECefVPYuGkpotnM08LHUl2TlpaImLWZgFTeEcPIrzvBj7ctL84YcpOrHzlG\nkllF5JBedk5VhaWCYNdWnS/iHxtaGqIvk0vYFmIKT2dGQMG98gf/UpQfhBk4\ngcFS+JGMlyYiJTQjmIxQEDyNaPo3yD+dmJvD8/W8nU8hGReFufFJMKutTINS\n2/N0tps4SchnmkUm/t7QzbLdPxHH4mzQ5/Vt9bmk5nkHANPSRYDlQOVYaO8S\nS0t5/JjVSrz2LDB9rO7yjKbNmp9rkyvoeiWQzMOlpmtA4TI5Km1FR+1CYbDP\nGV+xbE9qmT1nwIbmJA6+MOxQSIIbYg3g3BvU+e8b+aH1rZVWo8Rr3sjXqS2Z\nHqVE\r\n=QvRE\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBNclBUQFRgwOQLC7cCcRDcQCm+rBtR6hwY0MLMJw86aAiEA2Af+acWCfgERrB+LeUvPpbbbLfB57RoNuJt93/J4dPQ="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.0-1_1585528901882_0.8375244583139065"},"_hasShrinkwrap":false},"5.2.0-2":{"name":"react-recollect","version":"5.2.0-2","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","build:watch:dev":"rollup -c --watch --configUse=dev","build:watch:tests":"rollup -c --watch --configUse=tests","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run checkall","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run checkall"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.0","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react":">=15.3"},"gitHead":"8504b07e353254b3247908acbc65cbd512ced344","readme":"![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n- [Loading with a script tag](#loading-with-a-script-tag)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you wanted to sync your store to local storage, you could do the\nfollowing (anywhere in your app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  localStorage.siteData = JSON.stringify(e.store);\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit your store in the console.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nYou can play around with it in this [codesandbox](https://lxy1mz200l.csb.app),\nif you like.\n\n# Loading with a script tag\n\nYou can load Recollect via a `<script>` tag, if you like.\n\n```html\n<script src=\"https://unpkg.com/react-recollect\"></script>\n```\n\nYou should reference an exact version in the URL so that it can be cached. To\nget the URL with the version, [click here](https://unpkg.com/react-recollect)\nand copy that URL.\n\n[/demo/public/browser.html](./demo/public/browser.html) is a working example\nwith React and Babel.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\n- There's 100+ integration/unit tests in the [tests](./tests) directory.\n- There's a `/demo` directory with a Create React App site using Recollect. This\n  has a Cypress test suite.\n- There's [/demo/public/browser.html](./demo/public/browser.html) for manual\n  testing of the UMD build of Recollect.\n\n## How big is it?\n\n3—5 KB, depending on what else you've got installed. If you're coming from Redux\nland, you'll save about 1 KB in library size, but the big savings come from\ngetting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.0-2","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-vNsbh7B636UXiVaeUn3ACfjTq31ZQqPtIGSY4ZbHjeP/rxPfRFroxs+XX8ugVqz1odtS8AwFfWQeMhb8IPcoNQ==","shasum":"27acbf29135e1a5fed3739901826c8be6b084a63","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.0-2.tgz","fileCount":46,"unpackedSize":292236,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJegUZqCRA9TVsSAnZWagAA0K8QAIEKpymnrVNKZNQsKLUP\nz62fQBc82+8LBZP3f8ckhKBs82m4t4lJ8MAC6g33PsUfTkH7XFxWiZP3seop\n7zEQK/ftUf3TgSzNqUogo/+lL17SbgzQn81sSawNFBK6dhoh5PwE4TyZlZtX\n80/8g+bCWTAjWfbLrcQBqKfegcWE44s6/WXbfImf27cI8nDU/GrkiuQMNN/2\nOgT9QUXkNibp8LJiMQDQjuNOJQhn4fI48+wySc+18YSlMHHviYSCmwI7+ogM\nZxI62pMQ+n0XjHGB+14VKJXOH8+23vxh+bcwpnPgQ02scx9HC8btYo/NdwnE\nHV2BmNccZyo8Wkqo7eBjBw8l6ZKArEQfO/4n1kSrRy4GkPS5aAxgg1BcSnbY\nEkIJDDWcRc8Av7HyCTvN37CvPVOk5zLWtv9xLJjxOBzvaA9e3GIuWGgjLq/u\nSuyPvZf7K+eYOpDkZ7TNVIrThqlcRsrnufFaf3vQy82/Ds2o6+laauh25u/Q\nm88xMY514+iEAf0bBvaNv2rN9bPD1XLCGgj1xe8E5pC9G2jIdAG5os8EZiVg\nfNUMOmKwMdf2Eg7JjZSgqhpFBTwkIy57Hv6BMQkN51H61PlS+vdypeSp2Rwi\ngIgMDhMqFu1NEKcXkbWuvkNAKcVrCDuqbpgANqncsQuS8CyiIoa0vQ3VXKLq\nlZi4\r\n=Ia+n\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCPqQxLVO6NOMHuu/uxMDZ+yMragRbj6beEkjkGkqwvxAIgMjzSlZEf7kG0VrR2FMCU++JF7DpY/2zQmhbWqjH3zuw="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.0-2_1585530474288_0.9468794689355879"},"_hasShrinkwrap":false},"5.2.0":{"name":"react-recollect","version":"5.2.0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","checkall":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run checkall","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run checkall"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.0","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react":">=15.3"},"gitHead":"b136eddde071d46ba9a6f898b576899b98dfecda","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.0","_nodeVersion":"10.19.0","_npmVersion":"6.13.4","dist":{"integrity":"sha512-VDOIsivl3fCJ/CiAB0lHxxnPrM6MempyHMwfTc6hxRtST9LiuWkV2/EbzzbGU2TIQtgksBwJSutJ+Hqlqqtjlw==","shasum":"72938ce91c5d913acd76dc57968ac54a9ba69c2a","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.0.tgz","fileCount":46,"unpackedSize":300368,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJegcguCRA9TVsSAnZWagAAftQP/jrsqmfaYb4q1eN0V0OR\nYe5G+0BZQjbIutjlKaaSJ8zK0Q7o6/7xerH2qpDu2V0PprwBEWKO5zKKjb+L\nwJurkHP45OfsFbD2ZCf90wFzpDb9ksX/IqcG94aOzycRoRGCzD445Pwm46c5\nASsE2dNrcoPn0ZewPz0hahv5jHBRCAzrNI+nzt1E9MfpDFPIhojKeyvc42mz\nC2ROVYnuT+naJoWyV2Xh3rtvzTB9SvbSb9yAlXEfwa73C7Ehc8IxgrRUiiUW\n7RXPvwWo82ztiAVIr2Dym2oHzbboA/u2p05ZNk2LBPughNjs7S2q0cDOPL2c\n7aPVXuAA3MWsNk0dOIfouIWD2tZdOAhGSyZleh1YHxVG/n9+yIE2fKxiNKag\nTlXUAfmDcYtA6komONo63Tr8NiR0Z19K4w65pSTYOkfQ+nl2RAcqD8FnPdSY\ndGurRK1W7ETn9z+NXepGBKhywqyPRt4A+HMybbzihf7mrj8U8j+GvsZ5/5eG\nOburcCzoK/d/AcuWVBJlrJXAJT6yeLFYSIwN9osjqB2r4rBv+SFUCp2NqtTC\nSDtN4cAg7B3QuIEtT358PP6fA3AJM0XYswc7Yydd2VRKIQ5iphhuPMffPy+E\nKqo4rgN0CMeiK7eP4WL0prrXxKRfNLhvUsUo85D4ZiRfeZzEpd9A1iL+zC40\nOe2F\r\n=KeMB\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIDtBahWMs13GAvpr5s7LcrmK2RdTfcp7qFvRoODnc8HXAiEAnyCIdfxPSECa0yhpt27eFxMoAKjDIPr87piiVRifhtY="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.0_1585563693598_0.6768658937486747"},"_hasShrinkwrap":false},"5.2.2-0":{"name":"react-recollect","version":"5.2.2-0","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","check:all":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run check:all","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run check:all"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.0","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react-dom":">=15.3","react":">=15.3"},"readme":"![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n  - [NPM](#npm)\n  - [CDN](#cdn)\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# Installation\n\n## NPM\n\nInstall with npm:\n\n```\nnpm install react-recollect\n```\n\nOr Yarn:\n\n```\nyarn add react-recollect\n```\n\nYou can then import it in the usual ways:\n\n```js\nimport { collect, store } from 'react-recollect';\n\n// or\nconst { collect, store } = require('react-recollect');\n```\n\n## CDN\n\nYou can also load Recollect from the [unpkg CDN](https://unpkg.com).\n\n```html\n<script src=\"https://unpkg.com/react-recollect\"></script>\n```\n\nThis will create a global `ReactRecollect` object. See\n[demo/public/browser.html](./demo/public/browser.html) for a working example\nwith React and Babel.\n\nIt's a good idea to reference an exact version in the URL, so that it can be\ncached. [Click here](https://unpkg.com/react-recollect) to get the full URL.\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you want to save the current page to local storage when a\nparticular value in the store changes, you could do the following (anywhere in\nyour app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  if (e.changedProps.includes('currentPage')) {\n    localStorage.currentPage = e.store.currentPage;\n  }\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit the Recollect store in your browser's\nconsole.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties, available in development or production:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nDuring development there are two more methods to help you inspect your app:\n\n- `getListenersByComponent()` will show you which store properties each\n  component is subscribed to.\n- `getComponentsByListener()` is the inverse: it will show you which components\n  are subscribed to which store properties.\n\nYou can optionally pass a string or regular expression to filter the results.\n\n```js\n// Which components are subscribed to the user's status\n__RR__.getComponentsByListener('user.status');\n\n// What is <MyComponent> subscribed to?\n__RR__.getListenersByComponent('MyComponent');\n\n// What about the <Task> component where the prop `taskId` is 2?\n__RR__.getListenersByComponent('Task2', (props) => props.taskId);\n```\n\nCheck out [the debug test suite](./tests/unit/debug.test.tsx) for more examples.\n\nYou can play around with `__RR__` in\n[the Recollect demo site](https://0q4fo.csb.app).\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\n- There's 100+ integration/unit tests in the [tests](./tests) directory.\n- There's a `/demo` directory with a Create React App site using Recollect. This\n  has a Cypress test suite.\n- There's [/demo/public/browser.html](./demo/public/browser.html) for manual\n  testing of the UMD build of Recollect.\n\n## How big is it?\n\n3—5 KB, depending on what else you've got installed. If you're coming from Redux\nland, you'll save about 1 KB in library size, but the big savings come from\ngetting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"d9e721122e13cd690cac7e46c1fbe9835b2b2006","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.2-0","_nodeVersion":"13.12.0","_npmVersion":"6.14.4","dist":{"integrity":"sha512-GHez//hENsG1bcFj4oJVP+TG7isPHTbelj1qRecnRlJi52xeLExmWHFltMqoagCA9Btfv51ub2obVQ5MYCtAyA==","shasum":"c768c0842cc7f437dc1ae40cae6a2c0013e3c555","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.2-0.tgz","fileCount":49,"unpackedSize":304411,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJellxJCRA9TVsSAnZWagAAHV0QAIYAuNaHUd6naiV417Rb\n7BpNNUhrWjy8nPG9bdnFCRcqDUC0F0ZkqmG8fEuCLY+Dpc+zLEbXgITkLRQr\nIWVQE2AzpYSAagBsb5IKRZQU2eqaOX9v4HLucRsYg2KjCQon5tvT3l/238Ul\netkGIqlcD5vdW3/ySOtBCyW1ozgiibjLzr2BXN95mMJ8CvQqhujiRC9R2u4k\nIHFueZ17MMEEeHX1o1VfdMKU0tcfANgqBofH3FrdeZprcnUjD02Yb8SYe4DL\nXGuoZu0F7VNOO+MJ+v0P9Nks6u1sf0TpGBX0nzGiB2BIqwhp6ooxDIeWupjr\neXDhS5KoMrw0ZVQypuFFKuxgI9o2Q44RxxAPVfeRJWMlTeLamos/wmrnIvrf\n7G3TCwxJCZMwuPKkfpTxTNK7+t+hiW3Q9gHoMz2wLYpzaERbjsrZ1mSVbm3+\nWRnlScvvtWboIkBrLh/STxdS68zW295WRk3297eNjHMefHsd0gcXKkHcwbwH\nSFvUtDTbZgG6bG4MBaMKPD+BRv4hFsWAiiM5V7GHXLO/W6KLdr1LNH0MSmbH\ndA3WXV/i3anGOJmTjYZS9aET8BTgchLZcChJK7+b88LdV4+dLtC0cltiM+Hh\ngfHDgdb4QoMosPvKa0d7dRZ5Rt/haHgQHilkUiQz9rlOBTd1TvnXTqu30vDp\nZiVh\r\n=DIg6\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIH3LX/k/cs1mNPinONYQu9YjHpV8LN64T7VQhEE/AiPGAiEAv3U7HBkwV4wJaaZ6iE+hJM73En3fhSlby59yem/rWPI="}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.2-0_1586912329201_0.28818854548559836"},"_hasShrinkwrap":false},"5.2.2":{"name":"react-recollect","version":"5.2.2","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","check:all":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run check:all","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run check:all"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.0","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react-dom":">=15.3","react":">=15.3"},"readme":"![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n  - [NPM](#npm)\n  - [CDN](#cdn)\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# Installation\n\n## NPM\n\nInstall with npm:\n\n```\nnpm install react-recollect\n```\n\nOr Yarn:\n\n```\nyarn add react-recollect\n```\n\nYou can then import it in the usual ways:\n\n```js\nimport { collect, store } from 'react-recollect';\n\n// or\nconst { collect, store } = require('react-recollect');\n```\n\n## CDN\n\nYou can also load Recollect from the [unpkg CDN](https://unpkg.com).\n\n```html\n<script src=\"https://unpkg.com/react-recollect\"></script>\n```\n\nThis will create a global `ReactRecollect` object. See\n[demo/public/browser.html](./demo/public/browser.html) for a working example\nwith React and Babel.\n\nIt's a good idea to reference an exact version in the URL, so that it can be\ncached. [Click here](https://unpkg.com/react-recollect) to get the full URL.\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you want to save the current page to local storage when a\nparticular value in the store changes, you could do the following (anywhere in\nyour app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  if (e.changedProps.includes('currentPage')) {\n    localStorage.currentPage = e.store.currentPage;\n  }\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit the Recollect store in your browser's\nconsole.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties, available in development or production:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle.\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith with `[[Handler]]` and `[[Target]]` props. These are the proxies. All you\nneed to know is that `[[Target]]` is the actual object you put in the store.\n\nDuring development there are two more methods to help you inspect your app:\n\n- `getListenersByComponent()` will show you which store properties each\n  component is subscribed to.\n- `getComponentsByListener()` is the inverse: it will show you which components\n  are subscribed to which store properties.\n\nYou can optionally pass a string or regular expression to filter the results.\n\n```js\n// Which components are subscribed to the user's status\n__RR__.getComponentsByListener('user.status');\n\n// What is <MyComponent> subscribed to?\n__RR__.getListenersByComponent('MyComponent');\n\n// What about the <Task> component where the prop `taskId` is 2?\n__RR__.getListenersByComponent('Task2', (props) => props.taskId);\n```\n\nCheck out [the debug test suite](./tests/unit/debug.test.tsx) for more examples.\n\nYou can play around with `__RR__` in\n[the Recollect demo site](https://0q4fo.csb.app).\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(TaskList);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are discussed in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\n- There's 100+ integration/unit tests in the [tests](./tests) directory.\n- There's a `/demo` directory with a Create React App site using Recollect. This\n  has a Cypress test suite.\n- There's [/demo/public/browser.html](./demo/public/browser.html) for manual\n  testing of the UMD build of Recollect.\n\n## How big is it?\n\n3—5 KB, depending on what else you've got installed. If you're coming from Redux\nland, you'll save about 1 KB in library size, but the big savings come from\ngetting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","gitHead":"642cc9aa358145f36215669477dbab4bf66a268d","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.2","_nodeVersion":"13.12.0","_npmVersion":"6.14.4","dist":{"integrity":"sha512-kWMOjudqoTvGSLNt6GbNn0LApUzBOqlMH37EIwBfhr8c/2yBVBsgzahf/AKL7uFmoR5AF2whSbIpGED8qgY52w==","shasum":"094cf8c2f747596ddb2ccba5337b62a82b32cf4b","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.2.tgz","fileCount":49,"unpackedSize":304409,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJell/TCRA9TVsSAnZWagAAv/gP/ib7sdFjc5F76MNlPFY/\nNgvScIG0AqyC/85Gffzm/HP9AO+hKGj9aQRn6CBdyJokqDpHQWblmnoGCwtN\nNHrj3hARmQKUAb1AmcsBt0vN/ctpoTsOxodMBol5v/Z8DHgaz1fXw/eeOi2q\npxgN60DlqeVsut+X6N2SvEsg5oOOm1UDAv1MozrcphDeNbRmgOf5NpUBvei6\nhf36BnTn4FuVy4tiMncijIufQZKogZ6fEWeUVYVwxHPZQXnrOio9lmk+i8mZ\ny5ie9lPnAtKoed9XcHolDTDWg1CQ0z/S31XFFCaIHcKOtn9mkl+4yvsg8z9q\n7PJfMXnULGLTCnGVlmjzUtwPeTfX6SPwTKr4q8UglUg3YLp3Fe4Yh91kj5nh\nak1IQxlyr9HFQ2sT80riy+kjiHlab0AYO20XA0yzUATHpCzUZvm9urXc5JMY\ntnVCh/rTYm3GRIO2sgsDRjpg0oOgPaeVHI6aFJ+F40Yg1Vj2fZnqVBA5Bm7P\n9w0aTBTMt/1KWeAp3Ytm1cBIGMOWLtg7LHlTK/dL9srmkpNVASPDHrV11VJq\ncDxNbW7oL7SnLsQjrCRMGNKDm8qd9mumJk6XtLCOkURvj9R5SJH9f+jX1uL7\nCzEqIB0YVrUTH0RqbYPkQuW9knUpMpOuGcY8EAIYFgdSxL4mvf7gIN+dUwd6\nKXoV\r\n=qLIA\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQCzqjWd/aGMGuOHJaEF8OReXfkcUEFMWC1BLZMzD5tAHgIhAJ0+dlOsUlk3coXf2AD+3mQbIj6ymL7SdpQosVrOg337"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.2_1586913234453_0.31135168665720525"},"_hasShrinkwrap":false},"5.2.3":{"name":"react-recollect","version":"5.2.3","description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"license":"MIT","author":{"name":"David Gilbertson"},"main":"index.cjs.js","module":"index.esm.js","unpkg":"dist/umd/index.production.js","types":"dist/types/index.d.ts","scripts":{"build":"rollup -c","build:watch":"rollup -c --watch","check:all":"npm run build && npm run lint && npm run test:ci","lint":"eslint src tests --fix --cache --ext js,ts,tsx --max-warnings=0","nodeTest":"node ./tests/integration/nodeJs.js","prepublishOnly":"npm run check:all","readme:toc":"doctoc README.md","test":"npm run nodeTest && jest ./test --watch","test:ci":"npm run nodeTest && jest ./test --ci","preversion":"npm run check:all"},"dependencies":{"@types/prop-types":"^15.7.3","hoist-non-react-statics":"^3.3.0","prop-types":"^15.7.2"},"devDependencies":{"@rollup/plugin-commonjs":"^11.0.2","@rollup/plugin-node-resolve":"^7.1.1","@rollup/plugin-replace":"^2.3.1","@testing-library/jest-dom":"^5.1.1","@testing-library/react":"^10.0.1","@types/hoist-non-react-statics":"^3.3.1","@types/jest":"^25.1.4","@types/node":"^13.9.3","@types/react":"^16.9.23","@types/react-dom":"^16.9.5","@types/testing-library__dom":"^7.0.0","@types/testing-library__jest-dom":"^5.0.2","@types/testing-library__react":"^9.1.3","@typescript-eslint/eslint-plugin":"^2.24.0","@typescript-eslint/parser":"^2.24.0","doctoc":"^1.4.0","eslint":"^6.8.0","eslint-config-airbnb":"^18.1.0","eslint-config-airbnb-typescript":"^7.2.0","eslint-config-prettier":"^6.10.1","eslint-plugin-import":"^2.20.1","eslint-plugin-jsx-a11y":"^6.2.3","eslint-plugin-prettier":"^3.1.2","eslint-plugin-react":"^7.19.0","eslint-plugin-react-hooks":"^2.4.0","jest":"^25.1.0","lodash":"^4.17.15","prettier":"^2.0.1","react":"^16.13.0","react-dom":"^16.13.0","rollup":"^2.2.0","rollup-plugin-bundle-size":"^1.0.3","rollup-plugin-terser":"^5.3.0","rollup-plugin-typescript2":"^0.27.0","size-plugin":"^2.0.1","source-map-loader":"^0.2.4","ts-jest":"^25.2.1","ts-loader":"^6.2.2","tslib":"^1.11.1","typescript":"^3.8.3"},"peerDependencies":{"react-dom":">=15.3","react":">=15.3"},"gitHead":"b20cb1779189f85e10e2469588b241e836918ed0","homepage":"https://github.com/davidgilbertson/react-recollect#readme","_id":"react-recollect@5.2.3","_nodeVersion":"13.12.0","_npmVersion":"6.14.4","dist":{"integrity":"sha512-hoSjmH1vxx0jQ0X/gv2/luixSz65X/tdAIO5WJ1Pizo/PfYufY+2sNm1Oawve81B03viOs8Rt45SXiOPOSExxg==","shasum":"08ee09cf698696bc58038fdbb243a8f7ff64ed1a","tarball":"https://registry.npmjs.org/react-recollect/-/react-recollect-5.2.3.tgz","fileCount":54,"unpackedSize":325987,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJeoNXfCRA9TVsSAnZWagAAFzcP/AtsoWCSBc3JcjEaPLt4\nKs7QNDGBJwa5awnQC3w2Jq5Z8ZGZn+lMGdO4G3PuZJMDBi0W/W3k0DRqvHLI\nfxjTsJ/Wxls0LOQL6D7OvncZy/IbGeBLyP6woczZ7baUrUKx8Wb5vmpHOcYd\nBDCnBBpAS60aJRiEdyCyXCkzHnUD2PXuvgld/wH6NvViy44dy5byQC8o0u6T\n64u7MXxy6ij49Tkj171ZqvlsvOkqoWU+qaDsMkP/gYGnWTUstlWPlkRAB7Ty\nKxWdVWUe7hE6xicq3i++EN+Ol+ySoUGFsD1mLwyKOFu6vcTuWchiwmuy+5FR\n4r1g2Jn8DR4voIA9bBjX1+yLeA4LVXO76IHCIZh5aPLAJII8q81ZY6AcVeI/\n2KyAh0P3LQFjzLjWy5VcjZ9EaRE/lL0jfU8yNzRIEwja3uYJ0RloGwbN+JVM\nHZSpDVsA4f/ZUKMQGj2K1pg04DF9MBHWrXMX/FYv7qy6N6F1sUsJQnYxZN2t\nKMMuDWZ/3ofGI0pNRES3BAtNBAf5mfrb3Ev4CF7IMC6qZ6/NC0hEOqxXL/89\nc1rE/pEROWvXY6DyCbjWzZDu4QbHd017EpNg0ggOYR1DGx5E6X/zjQgXX3A1\n8pOUgJery4Sa+GcLXxLKm2BLBEwgxIG6OqDNh4HRlxHPLPGQgimt6/4DSpak\nDGLP\r\n=91MN\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQC/Z5RFldOV37qfeVXkKBydpNMCLL5WqbyfDg5H6SUIXwIhAKjOeIKVzk8W/RNm4RJqRAcKG0S9dvXNb6Sd8akiwO0L"}]},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"_npmUser":{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"},"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/react-recollect_5.2.3_1587598815079_0.8067056911777462"},"_hasShrinkwrap":false}},"time":{"created":"2018-10-29T23:18:01.404Z","1.0.0":"2018-10-29T23:18:01.602Z","modified":"2022-05-15T02:18:21.111Z","1.0.1":"2018-10-29T23:44:54.252Z","1.1.0":"2018-10-30T02:46:15.004Z","1.1.1":"2018-11-02T03:01:40.465Z","1.1.2":"2018-11-04T03:34:27.785Z","1.1.3":"2018-11-04T03:54:26.678Z","2.0.0":"2018-11-11T07:53:35.000Z","2.1.0":"2018-11-13T08:13:44.429Z","2.1.1":"2018-11-16T08:34:26.480Z","2.1.2":"2018-11-16T08:39:56.029Z","2.2.0":"2018-11-17T07:26:42.746Z","2.2.1":"2018-11-18T09:14:11.082Z","2.2.2":"2018-11-18T09:21:44.760Z","2.3.0":"2018-11-23T21:27:13.098Z","2.4.0":"2018-11-24T10:10:35.855Z","3.0.0":"2018-11-25T04:37:33.780Z","3.0.1":"2018-11-25T05:38:13.780Z","3.0.2":"2018-11-25T06:25:02.287Z","3.0.3":"2018-12-01T07:12:41.534Z","3.1.0":"2018-12-01T22:06:46.051Z","3.1.1":"2018-12-02T01:15:20.355Z","3.1.2":"2019-01-28T03:32:02.224Z","3.1.3":"2020-02-13T01:24:24.401Z","4.0.0-beta.0":"2020-03-05T00:12:19.970Z","4.0.0-beta.1":"2020-03-05T00:22:39.041Z","4.0.0":"2020-03-05T02:25:47.330Z","4.0.1":"2020-03-05T07:46:27.492Z","4.0.2":"2020-03-07T06:17:55.828Z","4.0.3":"2020-03-09T08:07:35.294Z","4.0.4":"2020-03-10T02:45:17.255Z","5.0.1-beta.0":"2020-03-15T04:30:05.684Z","5.0.0-beta.0":"2020-03-15T05:39:20.432Z","5.0.0-beta.1":"2020-03-15T06:06:15.234Z","5.0.0":"2020-03-15T06:35:23.215Z","5.1.0":"2020-03-19T07:50:17.767Z","5.1.1":"2020-03-19T22:37:52.185Z","5.1.2":"2020-03-20T02:44:49.719Z","5.1.3":"2020-03-20T10:28:13.073Z","5.1.4":"2020-03-21T06:51:36.116Z","5.1.5":"2020-03-24T21:29:40.779Z","5.1.6-0":"2020-03-24T21:40:07.779Z","5.1.6":"2020-03-24T21:45:50.050Z","5.2.0-0":"2020-03-27T04:03:04.306Z","5.2.0-1":"2020-03-30T00:41:42.092Z","5.2.0-2":"2020-03-30T01:07:54.547Z","5.2.0":"2020-03-30T10:21:33.775Z","5.2.2-0":"2020-04-15T00:58:49.420Z","5.2.2":"2020-04-15T01:13:54.707Z","5.2.3":"2020-04-22T23:40:15.206Z"},"maintainers":[{"name":"davidgilbertson","email":"gilbertson.david@gmail.com"}],"description":"Simple state management for react","keywords":["flux","react","redux","state","state management"],"license":"MIT","readme":"![version](https://img.shields.io/github/package-json/v/davidgilbertson/react-recollect?label=Version)\n![Tests](https://github.com/davidgilbertson/react-recollect/workflows/Node.js%20CI/badge.svg)\n\n# React Recollect\n\n## What?\n\nRecollect is a state management library for React, an alternative to Redux.\n\n## Why?\n\nRecollect aims to solve two problems with the traditional React/Redux approach:\n\n1. Immutability logic is verbose, complicated, and prone to bugs.\n2. Developers must write code to define which parts of the store a component\n   plans to use. Even then, components can be re-rendered when they don't use\n   the data that has just changed.\n\n## How?\n\n1. The Recollect store is immutable, but the implementation is hidden. So, you\n   can interact with the store as though it were a plain JavaScript object.\n2. Recollect records access to the store during the render cycle of a component.\n   When a property in your store changes, only components that use that property\n   are re-rendered.\n\nThe result is simpler code and a faster app. Take it for a spin in this\n[CodeSandbox](https://codesandbox.io/s/github/davidgilbertson/react-recollect/tree/master/demo).\n\n---\n\n**Caution:** there is no support for any version of IE, Opera mini, or Android\nbrowser 4.4 (because Recollect uses the `Proxy` object). Check out the latest\nusage stats for proxies at [caniuse.com](https://caniuse.com/#feat=proxy).\n\n# Quick start\n\n```\nnpm i react-recollect\n```\n\nThe `store` object and the `collect` function are all you need to know to get\nstarted.\n\nThe store is where your data goes; you can treat it just like you'd treat any\nJavaScript object. You can import, read from, and write to the store in any\nfile.\n\nHere's some code doing normal things with the normal-looking `store`:\n\n```js\nimport { store } from 'react-recollect';\n\nstore.tasks = ['one', 'two', 'three']; // Fine\n\nstore.tasks.push('four'); // Good\n\nstore.site = { title: 'Page one' }; // Acceptable\n\nObject.assign(store.site, { title: 'Page two' }); // Neato\n\nstore.site.title += '!'; // Exciting!\n\ndelete store.site; // Seems extreme, but works a treat\n\nstore = 'foo'; // Nope! (can't reassign a constant)\n```\n\n> Play with this code in a\n> [CodeSandbox](https://codesandbox.io/s/normal-store-doing-normal-things-5wz77)\n\nThese operations behave just like you'd expect them to, except none of them\n_mutate_ the store contents. In fact, it's impossible to mutate the data in a\nRecollect store.\n\nNext up: the `collect` function. This wraps a React component, allowing\nRecollect to take care of it. This will provide the store as a prop, and update\nthe component when it needs updating.\n\nHere's `collect` and `store` working together:\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst TaskList = ({ store }) => (\n  <div>\n    {store.tasks.map((task) => (\n      <div>{task.name}</div>\n    ))}\n\n    <button\n      onClick={() => {\n        store.tasks.push({\n          name: 'A new task',\n          done: false,\n        });\n      }}\n    >\n      Add a task\n    </button>\n  </div>\n);\n\nexport default collect(TaskList);\n```\n\nCongratulations my friend, you've finished learning Recollect. I am very proud\nof you.\n\nGo have a play, and when you're ready for more readme, come back to read on.\n\nIf you've got a question, make sure to read the [FAQ](#faq) to see if your Q is\nFA. Otherwise, open a GitHub issue.\n\n---\n\n<!-- START doctoc generated TOC please keep comment here to allow auto update -->\n<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->\n\n- [Installation](#installation)\n  - [NPM](#npm)\n  - [CDN](#cdn)\n- [API](#api)\n  - [`store`](#store)\n  - [`collect(ReactComponent)`](#collectreactcomponent)\n  - [`afterChange(callback)`](#afterchangecallback)\n  - [`initStore(data)`](#initstoredata)\n    - [On the server](#on-the-server)\n    - [In the browser](#in-the-browser)\n  - [`batch(callback)`](#batchcallback)\n  - [`useProps(propArray)`](#usepropsproparray)\n  - [`PropTypes`](#proptypes)\n  - [`window.__RR__`](#window__rr__)\n    - [Time travel](#time-travel)\n- [Usage with TypeScript](#usage-with-typescript)\n  - [Your store](#your-store)\n  - [Using collect](#using-collect)\n- [Project structure guidelines](#project-structure-guidelines)\n  - [Selectors](#selectors)\n  - [Updaters](#updaters)\n    - [Loading data with an updater](#loading-data-with-an-updater)\n    - [Asynchronous updaters](#asynchronous-updaters)\n    - [Testing an updater](#testing-an-updater)\n- [FAQ](#faq)\n  - [How does it work?](#how-does-it-work)\n  - [What sort of stuff can go in the store?](#what-sort-of-stuff-can-go-in-the-store)\n    - [Map and Set limitations](#map-and-set-limitations)\n  - [When will my components be re-rendered?](#when-will-my-components-be-re-rendered)\n  - [How many components should I wrap in `collect`?](#how-many-components-should-i-wrap-in-collect)\n  - [Can I use this with class-based components and functional components?](#can-i-use-this-with-class-based-components-and-functional-components)\n  - [Hooks?](#hooks)\n  - [Will component state still work?](#will-component-state-still-work)\n  - [Do lifecycle methods still fire?](#do-lifecycle-methods-still-fire)\n    - [Why isn't my `componentDidUpdate` code firing?](#why-isnt-my-componentdidupdate-code-firing)\n  - [Can I use this with `shouldComponentUpdate()`?](#can-i-use-this-with-shouldcomponentupdate)\n  - [Can I wrap a `PureComponent` or `React.memo` in `collect`?](#can-i-wrap-a-purecomponent-or-reactmemo-in-collect)\n  - [Can I use this with `Context`?](#can-i-use-this-with-context)\n  - [Can I use this with `ref`s?](#can-i-use-this-with-refs)\n  - [Can I have multiple stores?](#can-i-have-multiple-stores)\n  - [Can I use Recollect without React?](#can-i-use-recollect-without-react)\n  - [I'm getting a `no-param-reassign` ESLint error](#im-getting-a-no-param-reassign-eslint-error)\n  - [Tell me about your tests](#tell-me-about-your-tests)\n  - [How big is it?](#how-big-is-it)\n  - [Is reading/writing via a proxy slow?](#is-readingwriting-via-a-proxy-slow)\n- [Dependencies](#dependencies)\n- [Alternatives](#alternatives)\n- [Is it really OK to drop support for IE?](#is-it-really-ok-to-drop-support-for-ie)\n\n<!-- END doctoc generated TOC please keep comment here to allow auto update -->\n\n# Installation\n\n## NPM\n\nInstall with npm:\n\n```\nnpm install react-recollect\n```\n\nOr Yarn:\n\n```\nyarn add react-recollect\n```\n\nYou can then import it in the usual ways:\n\n```js\nimport { collect, store } from 'react-recollect';\n\n// or\nconst { collect, store } = require('react-recollect');\n```\n\n## CDN\n\nYou can also load Recollect from the [unpkg CDN](https://unpkg.com).\n\n```html\n<script src=\"https://unpkg.com/react-recollect\"></script>\n```\n\nThis will create a global `ReactRecollect` object. See\n[demo/public/browser.html](./demo/public/browser.html) for a working example\nwith React and Babel.\n\nIt's a good idea to reference an exact version in the URL, so that it can be\ncached. [Click here](https://unpkg.com/react-recollect) to get the full URL.\n\n# API\n\n## `store`\n\nThe `store` object that Recollect exposes is designed to behave like a plain old\nJavaScript object. But it's a bit different because it's immutable. You can\nwrite code as though you _were_ mutating it, but internally it will clone the\nparts of itself that it needs to clone to apply your changes, without mutating\nanything.\n\nWhen the store is then passed to a component, React can do its clever shallow\ncomparisons to know whether something has changed and update efficiently.\n\n## `collect(ReactComponent)`\n\nWhen you wrap a component in `collect`, Recollect will:\n\n- Provide the store object as a prop.\n- Collect information about the data the component needs to render (which\n  properties in the store it read while rendering).\n- Re-render the component when that data changes.\n\nInternally, Recollect 'subscribes' components to property 'paths'. For example,\nthis component would be subscribed to the `store.page.title` path and\nre-rendered when that property changes.\n\n```jsx harmony\nimport { collect } from 'react-recollect';\n\nconst Header = ({ store }) => (\n  <header>\n    <h1>{store.page.title}</h1>\n  </header>\n);\n\nexport default collect(Header);\n```\n\n## `afterChange(callback)`\n\n`afterChange` will call the provided callback whenever the store updates.\n\nThe callback receives an event object with these properties:\n\n- `store` — the store\n- `changedProps` — the 'paths' of the properties that changed. E.g.\n  `['tasks.2.done', 'tasks.4.done']`\n- `renderedComponents` — an array of the components that were updated\n\nFor example, if you want to save the current page to local storage when a\nparticular value in the store changes, you could do the following (anywhere in\nyour app).\n\n```js\nimport { afterChange } from 'react-recollect';\n\nafterChange((e) => {\n  if (e.changedProps.includes('currentPage')) {\n    localStorage.currentPage = e.store.currentPage;\n  }\n});\n```\n\n## `initStore(data)`\n\nThe `initStore` function will _replace_ the contents of the store with the\nobject you pass in.\n\n`data` is optional — if you don't pass anything, the store will be emptied\n(useful in tests).\n\nIf you're only using Recollect in the browser, you don't _need_ to use this, but\nit's handy to set the default state of your store. You can also use\n`Object.assign(store, { foo: 'bar' })` if you want to shallow-merge new data\ninto the store.\n\nWhen you render on the server though, you _do_ need to initialize the store,\nbecause unlike a browser, a server is shared between many users and state needs\nto be fresh for each request.\n\n### On the server\n\nHere's a minimal implementation of server-side rendering with Express and\nRecollect.\n\n```jsx harmony\n// Create an express app instance\nconst app = express();\n\n// Read the HTML template on start up (this is the create-react-app output)\nconst htmlTemplate = fs.readFileSync(\n  path.resolve(__dirname, '../../build/index.html'),\n  'utf8'\n);\n\n// We'll serve our page to requests at '/'\napp.get('/', async (req, res) => {\n  // Fetch some data\n  const tasks = await fetchTasksForUser(req.query.userId);\n\n  // Populate the Recollect store (discarding any previous state)\n  initStore({ tasks });\n\n  // Render the app. Components will read from the Recollect store as usual\n  const appMarkup = ReactDOMServer.renderToString(<App />);\n\n  // Serialize the store (replacing left tags for security)\n  const safeStoreString = JSON.stringify(store).replace(/</g, '\\\\u003c');\n\n  // Insert the markup and the data into the template\n  const htmlWithBody = htmlTemplate.replace(\n    '<div id=\"root\"></div>',\n    `<div id=\"root\">${appMarkup}</div>\n    <script>window.__PRELOADED_STATE__ = ${safeStoreString};</script>`\n  );\n\n  // Return the rendered page to the user\n  res.send(htmlWithBody);\n});\n```\n\nIt's important that you populate the store using `initStore`, and do so before\nrendering your app with `ReactDOMServer.renderToString()`.\n\nThis is because your Node server might receive several requests from several\nusers at the same time. All of these requests share the same global state,\nincluding the `store` object.\n\nSo, you must make sure that for each request, you empty the store, populate it\nwith the appropriate data for the request, and render the markup at the same\ntime. And by 'at the same time', I mean _synchronously_.\n\n### In the browser\n\nIn the entry point to your app, right before you call `ReactDOM.hydrate()`, call\n`initStore()` with the data that you sent from the server:\n\n```jsx harmony\nimport { initStore } from 'react-recollect';\n\n// other stuff\n\ninitStore(window.__PRELOADED_STATE__);\n\nReactDOM.hydrate(<App />, document.getElementById('root'));\n```\n\nThis will take the data that you saved in the DOM on the server and fill up the\nRecollect store with it. You should only init the store once, before the initial\nrender.\n\nNote that `initStore` will trigger a render of collected components where\napplicable, and will fire `afterChange`.\n\n## `batch(callback)`\n\nThe `batch` function allows you to update the store multiple times, and be\nguaranteed that components will only be updated after all updates are made.\n\nThe callback function will be called immediately and should only contain\nsynchronous code.\n\n```js\nimport { batch } from 'react-recollect';\n\nconst fetchData = async () => {\n  const { posts, users, meta } = await fetch('/api').then((response) =>\n    response.json()\n  );\n\n  batch(() => {\n    store.posts = posts;\n    store.users = users;\n    store.meta = meta;\n  });\n\n  // now a render will be triggered for any components that use this data\n};\n```\n\nNote that React already does a good job of batching multiple updates into a\nsingle render cycle. So only clutter up your code with `batch` if it results in\nan actual performance improvement.\n\n## `useProps(propArray)`\n\nIn most cases, you can rely on Recollect to know what data your component\nrequires to render. However, Recollect can't know that your component will\nrequire a property in the _future_. If you reference a property:\n\n- in `componentDidUpdate` (and nowhere else), or\n- in UI that is only revealed after a change in state (perhaps a modal or\n  drop-down)\n\n... then Recollect won't know about it and your component won't be subscribed to\nchanges in that property.\n\nYou can tell Recollect _“I want to know if any of these properties change”_ by\npassing an array of store objects to the `useProps` function, like so:\n\n```js\nimport { collect, useProps } from 'react-recollect';\n\nconst MyComponent = ({ store }) => {\n  const [showHiddenMessage, setShowHiddenMessage] = useState(false);\n\n  // \"This component might read `store.hiddenMessage` in the future\"\n  useProps([store.hiddenMessage]);\n\n  return (\n    <div>\n      {showHiddenMessage && <p>{store.hiddenMessage}</p>}\n\n      <button onClick={() => setShowHiddenMessage(true)}>\n        Show hidden message\n      </button>\n    </div>\n  );\n};\n\nexport default collect(MyComponent);\n```\n\nAlthough `useProps` starts with the word 'use', it doesn't require React's Hooks\nmechanism, so it works just fine in versions before React 16.8. (For the\ncurious, the implementation is literally just `propArray.includes(0)`.)\n\nCheck out [these tests](tests/unit/useProps.test.tsx) for more usage examples.\n\n## `PropTypes`\n\nAs you've learnt by now, Recollect works by 'recording' which properties your\ncomponent reads from the store while it renders. This poses a problem if you use\nthe `prop-types` library, because it is going to read _every property_ that you\ndefine in your prop types.\n\nThis could result in your component being subscribed to changes in a property it\ndoesn't use, potentially concealing a problem that would only become apparent in\nproduction (where prop types aren't checked).\n\nFor this reason, `react-recollect` exports a proxied version of `prop-types`.\nIt's exactly the same as the normal `prop-types` library, except that Recollect\nwill pause its recording while your props are being checked.\n\n```jsx harmony\nimport { PropTypes } from 'react-recollect';\n\nconst MyComponent = (props) => <h1>{props.title}</h1>;\n\nMyComponent.propTypes = {\n  title: PropTypes.string.isRequired,\n};\n\nexport default MyComponent;\n```\n\nWe recommended that you uninstall `prop-types` from your project and replace its\nusages with the Recollect version. That way no one can accidentally use the\n'wrong' `prop-types` (if they didn't get this far in the readme).\n\nIf you use `@types/prop-types` you can uninstall that too, the types are built\ninto `react-recollect`.\n\n## `window.__RR__`\n\nUse `window.__RR__` to inspect or edit the Recollect store in your browser's\nconsole.\n\n`__RR__` does not form part of the official API and should not be used in\nproduction. It might change between versions without warning and without\nrespecting semver.\n\nIt has these properties, available in development or production:\n\n- `debugOn()` will turn on debugging. This shows you what's updating in the\n  store and which components are being updated as a result, and what data those\n  components are reading. Note that this can have a negative impact on\n  performance if you're reading thousands of properties in a render cycle. Note\n  also that it will 'collapse' all other console logs into the output (important\n  for debugging, but not ideal a lot of the time).\n- `debugOff()` will surprise you\n- `internals` exposes some interesting things.\n\nVia the `internals` object, you can get a reference to the store, which can be\nhandy for troubleshooting. For example, typing\n`__RR__.internals.store.loading = true` in the console would update the store\nand re-render the appropriate components.\n\nIf you just log the store to the console, you will see a strange object littered\nwith `[[Handler]]` and `[[Target]]` props. These are the proxies. All you need\nto know is that `[[Target]]` is the actual object you put in the store.\n\nDuring development there are two more methods to help you inspect your app:\n\n- `getListenersByComponent()` will show you which store properties each\n  component is subscribed to.\n- `getComponentsByListener()` is the inverse: it will show you which components\n  are subscribed to which store properties.\n\nYou can optionally pass a string or regular expression to filter the results.\n\n```js\n// Which components are subscribed to the user's status\n__RR__.getComponentsByListener('user.status');\n\n// What is <MyComponent> subscribed to?\n__RR__.getListenersByComponent('MyComponent');\n\n// What about the <Task> component where the prop `taskId` is 2?\n__RR__.getListenersByComponent('Task2', (props) => props.taskId);\n```\n\nCheck out [the debug test suite](./tests/unit/debug.test.tsx) for more examples.\n\n### Time travel\n\n> <small>Added in `5.2.3`</small>\n\nYou can navigate through the history of changes to the store with the below\nfunctions (in your DevTools console):\n\n- `__RR__.back()` will go back to the state before the last store change.\n- `__RR__.forward()` will go forward again.\n- `__RR__.goTo(index)` will go to a particular index in the history.\n- `__RR__.getHistory()` will log out the entire history.\n- `__RR__.clearHistory()` clears the history.\n- `__RR__.setHistoryLimit(limit)` limits the number of store instances kept in\n  history. Defaults to `50`. Setting to `0` disables time travel. Is stored in\n  local storage.\n\nIf you update the store with `initStore` or execute multiple updates within the\n`batch` function callback, those changes are recorded as a single history event.\n\nNote that these time travel functions are only available during development, not\nin the production build.\n\n# Usage with TypeScript\n\n## Your store\n\nDefine the shape of your recollect `store` like this:\n\n```ts\ndeclare module 'react-recollect' {\n  interface Store {\n    someProp?: string[];\n    somethingElse?: string;\n  }\n}\n```\n\nPut this in a declarations file such as `src/types/RecollectStore.ts`.\n\n## Using collect\n\nComponents wrapped in `collect` must define `store` in `props` — use the\n`WithStoreProp` interface for this:\n\n```tsx\nimport { collect, WithStoreProp } from 'react-recollect';\n\ninterface Props extends WithStoreProp {\n  someComponentProp: string;\n}\n\nconst MyComponent = ({ store, someComponentProp }: Props) => (\n  // < your awesome JSX here>\n);\n\nexport default collect(MyComponent);\n```\n\nIf the only prop your component needs is `store`, you can use `WithStoreProp`\ndirectly.\n\n```tsx\nimport { WithStoreProp } from 'react-recollect';\n\nconst MyComponent = ({ store }: WithStoreProp) => <div>Hello {store.name}</div>;\n```\n\nRecollect is written in TypeScript, so you can check out the\n[integration tests](./tests/integration) if you're not sure how to implement\nsomething.\n\n(If you've got Mad TypeScript Skillz and would like to contribute, see if you\ncan work out how to resolve the `@ts-ignore` in\n[the collect module](./src/collect.tsx)).\n\n# Project structure guidelines\n\nThe ideas described in this section aren't part of the Recollect API, they're\nsimply a guide.\n\nTwo concepts are described in this section (neither of them new):\n\n- **Selectors** contain logic for retrieving and data from the store.\n\n- **Updaters** contain logic for updating the store. Updaters also handle\n  reading/writing data from outside the browser (e.g. loading data over the\n  network or from disk).\n\n![Cycle of life](cycle.png)\n\nIn a simple application, you don't need to explicitly think in terms of updaters\nand selectors. For example:\n\n- defining `checked={task.done}` in a checkbox is a tiny little 'selector'\n- executing `task.done = true` when a user clicks that checkbox is a tiny little\n  'updater'\n\nBut as your app grows, it's important to keep your components focused on UI —\nyou don't want 200 lines of logic in the `onClick` event of a button.\n\nSo there will come a point where moving code out of your components into\ndedicated files is necessary, and at this point, updaters and selectors will\nserve as useful concepts for organization.\n\nIn the examples below, I'll use a directory structure like this:\n\n```\n/my-app\n └─ src\n    ├─ components\n    ├─ store\n    │  ├─ selectors\n    │  └─ updaters\n    └─ utils\n```\n\n(Fun fact: _selector_ ends in 'or' because 'select' is derived from latin, while\n_updater_ ends in 'er' because it was made up in 1941 and 'or' had gone out of\nstyle.)\n\n## Selectors\n\nA simple case for a selector would be to return all incomplete tasks, sorted by\ndue date.\n\n```js\nexport const getIncompleteTasksSortedByDueDate = (store) => {\n  const tasks = store.tasks.slice();\n\n  return tasks\n    .sort((a, b) => a.dueDate - b.dueDate)\n    .filter((task) => !task.done);\n};\n```\n\nYou would then use this function by importing it and referencing it in your\ncomponent:\n\n```jsx harmony\nimport { getIncompleteTasksSortedByDueDate } from '../store/selectors/taskSelectors';\n\nconst TaskList = ({ store }) => {\n  const tasks = getIncompleteTasksSortedByDueDate(store);\n\n  return (\n    <div>\n      {tasks.map((task) => (\n        <Task key={task.id} task={task} />\n      ))}\n    </div>\n  );\n};\n```\n\nIn this example, I'm passing the `store` object into the `selector`. But you\ncould also do `import { store } from 'react-recollect'` in the selector file.\n(In Recollect version 4 and earlier, you _had_ to pass the store through. From\nv5 onwards, you can use `props.store` _or_ import the store.)\n\nMaybe we want to conditionally show either all tasks or only incomplete tasks.\nLet's create a second selector. And while we're at it, move repeated sorting\ncode out into its own function:\n\n```js\nconst getTasksSortedByDate = (tasks) => {\n  const sortedTasks = tasks.slice();\n\n  return sortedTasks.sort((a, b) => a.dueDate - b.dueDate);\n};\n\nexport const getAllTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks);\n\nexport const getIncompleteTasksSortedByDueDate = (store) =>\n  getTasksSortedByDate(store.tasks).filter((task) => !task.done);\n```\n\nAnd here's a more complex component with local state and a dropdown to show\neither all tasks or just those that aren't done:\n\n```jsx harmony\nclass TaskList extends PureComponent {\n  state = {\n    filter: 'all',\n  };\n\n  render() {\n    const { store } = this.props;\n\n    const tasks =\n      this.state.filter === 'all'\n        ? getAllTasksSortedByDueDate(store)\n        : getIncompleteTasksSortedByDueDate(store);\n\n    return (\n      <div>\n        {tasks.map((task) => (\n          <Task key={task.id} task={task} />\n        ))}\n\n        <select\n          value={this.state.filter}\n          onChange={(e) => {\n            this.setState({ filter: e.target.value });\n          }}\n        >\n          <option value=\"all\">All tasks</option>\n          <option value=\"incomplete\">Incomplete tasks</option>\n        </select>\n      </div>\n    );\n  }\n}\n```\n\nNow, when a user changes the dropdown, the component state will update, a\nre-render will be triggered, and as a result, a different selector will be used.\n\n## Updaters\n\nAn 'updater' is a function that updates the store in some way. As with\nselectors, you don't _need_ to use updaters, they're just an organizational\nconcept to minimize the amount of data logic you have in your component files.\n\nA simple case for an updater would be to mark all tasks as done in a todo app:\n\n```js\nimport { store } from 'react-recollect';\n\nexport const markAllTasksAsDone = () => {\n  store.tasks.forEach((task) => {\n    task.done = true;\n  });\n};\n```\n\nYou would reference this from a component by importing it then calling it in\nresponse to some user action:\n\n```jsx harmony\nimport { markAllTasksAsDone } from '../store/updaters/taskUpdaters';\n\nconst Footer = () => (\n  <button onClick={markAllTasksAsDone}>Mark all as done</button>\n);\n\nexport default Footer;\n```\n\nYou don't need to 'dispatch' an 'action' from an 'action creator' to a\n'reducer'; you're just calling a function that updates the store.\n\nAnd since these are just plain functions, they're 'composable'. Or in other\nwords, if you want an updater that calls three other updaters, go for it.\n\n### Loading data with an updater\n\nLet's create an updater that loads some tasks from an api when our app mounts.\nIt will need to:\n\n1. Set a loading indicator to true\n2. Fetch some tasks from a server\n3. Save the data to the store\n4. Set the loading indicator to false\n\n```js\nexport const loadTasksFromServer = async () => {\n  store.loading = true;\n\n  store.tasks = await fetchJson('/api/get-my-tasks');\n\n  store.loading = false;\n};\n```\n\nYou might call this function like so:\n\n```js\nimport { loadTasksFromServer } from '../store/updaters/taskUpdaters';\n\nclass TaskList extends React.Component {\n  componentDidMount() {\n    loadTasksFromServer();\n  }\n\n  render() {\n    // just render stuff\n  }\n}\n```\n\n### Asynchronous updaters\n\nDid you notice that we've already covered the super-complex topic of\nasynchronicity? You can update the Recollect store whenever you like, so you\ndon't need to do anything special to get asynchronous code to work.\n\n### Testing an updater\n\nLet's write a unit test to call our updater and assert that it put the correct\ndata in the store. The function we're testing is async, so our test will be\nasync too:\n\n```js\ntest('loadTasksFromServer should update the store', async () => {\n  // Execute the updater\n  await loadTasksFromServer();\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\nPretty easy, right?\n\nWe can make it less easy.\n\nMaybe we want to assert that `loading` was set to `true`, then the tasks loaded,\nand then `loading` was set to `false` again.\n\nWell, Recollect exports an `afterChange` function designed to call a callback\nevery time the store changes. If we pass it a Jest mock function, Jest will\nconveniently keep a record of each time the store changed.\n\nAlso, no one likes half an example, so here's the entire test file:\n\n```js\nimport { afterChange, store } from 'react-recollect';\nimport { loadTasksFromServer } from './taskUpdaters';\n\njest.mock('../../utils/fetchJson', () => async () => [\n  {\n    id: 1,\n    name: 'Fetched task',\n    done: false,\n  },\n]);\n\ntest('loadTasksFromServer should update the store', async () => {\n  // Create a mock\n  const afterChangeHandler = jest.fn();\n\n  // Pass the mock to afterChange. Jest will record calls to this function\n  // and therefore record calls to update the store.\n  afterChange(afterChangeHandler);\n\n  // Execute our updater\n  await loadTasksFromServer();\n\n  // afterChangeHandler will be called with the new version of the store and the path that was changed\n  const firstChange = afterChangeHandler.mock.calls[0][0];\n  const secondChange = afterChangeHandler.mock.calls[1][0];\n  const thirdChange = afterChangeHandler.mock.calls[2][0];\n\n  expect(firstChange.changedProps[0]).toBe('loading');\n  expect(firstChange.store.loading).toBe(true);\n\n  expect(secondChange.changedProps[0]).toBe('tasks');\n  expect(secondChange.store.tasks.length).toBe(1);\n\n  expect(thirdChange.changedProps[0]).toBe('loading');\n  expect(thirdChange.store.loading).toBe(false);\n\n  // Check that the final state of the store is what we expected\n  expect(store).toEqual(\n    expect.objectContaining({\n      loading: false,\n      tasks: [\n        {\n          id: 1,\n          name: 'Fetched task',\n          done: false,\n        },\n      ],\n    })\n  );\n});\n```\n\n# FAQ\n\n## How does it work?\n\nEvery object you add to the Recollect store gets wrapped in a `Proxy`. These\nproxies allow Recollect to intercept reads and writes. It's similar to defining\ngetters and setters, but far more powerful.\n\nIf you were to execute the code below, that `site` object would be wrapped in a\nproxy.\n\n```js\nstore.site = {\n  title: 'Page one',\n};\n```\n\n(Items are deeply/recursively wrapped, not just the top level object you add.)\n\nNow, if you execute the code `store.site.title = 'Page two'`, Recollect won't\nmutate the `site` object to set the `title` property. Recollect will block the\noperation and instead create a clone of the object where `title` is\n`'Page two'`. Recollect keeps a reference between the old and the new `site`\nobjects, so any attempt to read from or write to the 'old version' will be\nredirected to the 'new version' of that object.\n\nIn addition to intercepting _write_ operations, the proxies also allow Recollect\nto know when data is being _read_ from the store. When you wrap a component in\n`collect`, you're instructing Recollect to monitor when that component starts\nand stops rendering. Any read from the store while a component is rendering\nresults in that component being 'subscribed' to the property that was read.\n\nBringing it all together: when some of your code attempts to write to the store,\nRecollect will clone as described above, then notify all the components that use\nthe property that was updated, passing those components the 'next' version of\nthe store.\n\n## What sort of stuff can go in the store?\n\nYou can store anything that's valid JSON. If that's all you want to do, you can\nskip the rest of this section.\n\nRecollect will store data of any type, including (but not limited to):\n\n- `undefined`\n- `Map`\n- `Set`\n- `RegExp` objects\n- `Date` objects\n\nRecollect will _monitor_ changes to:\n\n- Primitives (string, number, boolean, null, undefined, symbol)\n- Plain objects\n- Arrays\n- Maps (see limitations below)\n- Sets (see limitations below)\n\nRecollect will store, but not monitor attempted mutations to other objects. For\nexample:\n\n- `store.date = new Date()` is fine.\n- `store.date.setDate(7)` will not trigger an update.\n- `store.uIntArray = new Uint8Array([3, 2, 1])` is fine.\n- `store.uIntArray.sort()` will not trigger an update.\n\nThe same applies to `WeakMap`, `DataView`, `ArrayBuffer` and any other object\nyou can think of.\n\nIf there's a data type you want to store and mutate that isn't supported, log an\nissue and we'll chat.\n\nOther things that aren't supported (or haven't been tested):\n\n- Functions (e.g. getters, setters, or other methods)\n- Class instances (if this would be useful to you, log an issue and we'll chat)\n- Properties defined with `Object.defineProperty()`\n- String properties on arrays, Maps and Sets (I don't mean string _keys_ in\n  maps, I mean actually creating a property on the object itself — a fairly\n  unusual thing to do)\n- `Proxy` objects (if this would be useful to you, log an issue and we'll chat)\n- Linking (e.g. one item in the store that is a reference to another item in the\n  store)\n\nYou can even store components in the store if you feel the need. This hasn't\nbeen performance tested, so proceed with caution.\n\n```jsx harmony\nconst Page = collect(({ store }) => {\n  const { Header, Footer, Button } = store.components;\n\n  return (\n    <React.Fragment>\n      <Header title=\"Page one\" />\n\n      <Button onClick={doSomething} />\n\n      <Footer />\n    </React.Fragment>\n  );\n});\n```\n\n### Map and Set limitations\n\nUpdating an object _key_ of a map entry will not always trigger a render of\ncomponents using that object. But in most cases you'd be storing your data in\nthe _value_ of a map entry, and that works fine.\n\nSimilarly, updating an object in a set may not trigger an update to components\nusing that object (adding/removing items from a set works fine).\n\n## When will my components be re-rendered?\n\nShort version: when they need to be, don't worry about it.\n\nLonger version: if a _property_ is changed (e.g.\n`store.page.title = 'Page two'`), any component that read that property when it\nlast rendered will be updated. If an object, array, map or set is changed (e.g.\n`store.tasks.pop()`) any component that read that object/array/map/set will be\nupdated.\n\nCheck out [tests/integration/updating](tests/integration/updating.test.tsx) for\nthe full suite of scenarios.\n\n## How many components should I wrap in `collect`?\n\nYou can wrap every component in `collect` if you feel like it. As a general\nrule, the more you wrap in `collect`, the fewer unnecessary renders you'll get,\nand the less you'll have to pass props down through your component tree.\n\nThere is one rule you must follow though: do not pass part of the store _into_ a\ncollected component as props. Don't worry if you're not sure what that means,\nyou'll get a development-time error if you try.\n[This issue explains why](https://github.com/davidgilbertson/react-recollect/issues/102).\n\nWhen dealing with components that are rendered as array items (e.g. `<Product>`s\nin a `<ProductList>`), you'll probably get the best performance with the\nfollowing setup:\n\n- Wrap the parent component in `collect`.\n- Don't wrap the child component in `collect`\n- Pass the required data to the child components as props.\n- Mark the child component as pure with `memo()` or `PureComponent`.\n\nWith this arrangement, when an item in the array changes (e.g. a product is\nstarred), Recollect will immutably update only that item in the store, and\ntrigger the parent component to update. React will skip the update on all the\nchildren that didn't change, so only the one child will re-render.\n\nFor a working example, see the\n[Recollect demo on CodeSandbox](https://codesandbox.io/s/lxy1mz200l).\n\n## Can I use this with class-based components and functional components?\n\nYep and yep.\n\n## Hooks?\n\nYep.\n\n## Will component state still work?\n\nYes, but be careful. If a change in state reveals some new UI, and a property\nfrom the store is only read in that UI (not elsewhere in the component) then\nRecollect won't be aware of it, and won't update your component if it changes.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Do lifecycle methods still fire?\n\nYep. Recollect has no effect on `componentDidMount`, `componentDidUpdate` and\nfriends.\n\n### Why isn't my `componentDidUpdate` code firing?\n\nIf you have a store prop that you _only_ refer to in `componentDidUpdate` (e.g.\n`store.loaded`), then your component won't be subscribed to changes in that\nprop. So when `store.loaded` changes, your component might not be updated.\n\nUse the [`useProps`](#usepropsproparray) function to make sure your component is\nsubscribed to changes in this property.\n\n## Can I use this with `shouldComponentUpdate()`?\n\nYes, but no, but you probably don't need to.\n\nThe\n[React docs](https://reactjs.org/docs/react-component.html#shouldcomponentupdate)\nsay of `shouldComponentUpdate()`:\n\n> This method only exists as a performance optimization. Do not rely on it to\n> “prevent” a rendering, as this can lead to bugs ... In the future React may\n> treat shouldComponentUpdate() as a hint rather than a strict directive, and\n> returning false may still result in a re-rendering of the component\n\nSo, if you're using `shouldComponentUpdate` for _performance_ reasons, then you\ndon't need it anymore. If the `shouldComponentUpdate` method is executing, it's\nbecause Recollect has _told_ React to update the component, which means a value\nthat it needs to render has changed.\n\n## Can I wrap a `PureComponent` or `React.memo` in `collect`?\n\nThere's no need. The `collect` function wraps your component in a\n`PureComponent` and there's no benefit to having two of them.\n\nIt's a good idea to wrap _other_ components in `PureComponent` or `React.memo`\nthough — especially components that are rendered in an array, like `<Todo>`. If\nyou have a hundred todos, and add one to the list, you can skip a render for all\nthe existing `<Todo>` components if they're marked as pure.\n\n## Can I use this with `Context`?\n\nYes. Recollect doesn't interfere with other libraries that use `Context`.\n\nYou shouldn't need to use `Context` yourself though. You have a global `store`\nobject that you can read from and write to anywhere.\n\n## Can I use this with `ref`s?\n\nYes, refs just work, as long as you don't use the reserved name 'ref' (React\nstrips this out). You can use something like `inputRef` instead. For an example,\nsee [this test](tests/react/forwardRefFc.test.tsx)\n\n## Can I have multiple stores?\n\nNo. There is no performance improvement to be had, so the desire for multiple\nstores is just an organizational preference. For this, you can use 'selectors'\nto focus on a subset of your store.\n\n## Can I use Recollect without React?\n\nYes! You can use `store` without using `collect`. Pair this with `afterChange`\nto have an object that notifies you when its changed. For an example, check out\n[tests/integration/nodeJs.js](./tests/integration/nodeJs.js)\n\n## I'm getting a `no-param-reassign` ESLint error\n\nYou can add 'store' as a special case in your ESLint config so that the rule\nallows you to mutate the properties of store.\n\n```json\n{\n  \"rules\": {\n    \"no-param-reassign\": [\n      \"error\",\n      {\n        \"props\": true,\n        \"ignorePropertyModificationsFor\": [\"store\"]\n      }\n    ]\n  }\n}\n```\n\nCheck out the `no-param-reassign` rule in this repo's\n[eslint config](./.eslintrc.json) for the syntax.\n\n## Tell me about your tests\n\n- There's 100+ integration/unit tests in the [tests](./tests) directory.\n- There's a `/demo` directory with a Create React App site using Recollect. This\n  has a Cypress test suite.\n- There's [/demo/public/browser.html](./demo/public/browser.html) for manual\n  testing of the UMD build of Recollect.\n\n## How big is it?\n\n3—5 KB, depending on what else you've got installed. If you're coming from Redux\nland, you'll save about 1 KB in library size, but the big savings come from\ngetting rid of all your reducers.\n\n## Is reading/writing via a proxy slow?\n\nSlow, no. Slower than vanilla object operations, yes.\n\nLet's quantify with a case study: in an app that has a store with ~80,000\nproperties, ~30,000 of them proxied objects and ~1,000 component listeners,\nupdating a chunk of data requiring 50 new proxies takes ~2 milliseconds (on\ndesktop). For that app, 2ms was considered insignificant compared to the ~90ms\nspent on the resulting render cycle.\n\nIf you're processing big data and facing performance troubles, open an issue and\nwe'll chat.\n\n# Dependencies\n\nRecollect has a peer dependency of React `>=15.3`.\n\n# Alternatives\n\nIf you want IE support, use Redux.\n\nIf you want explicit 'observables' and multiple stores, use MobX.\n\nIf you want a walk down memory lane, use Flux.\n\nAlso there is a library that is very similar to this one (I didn't copy,\npromise) called\n[`react-easy-state`](https://github.com/solkimicreb/react-easy-state).\n\n# Is it really OK to drop support for IE?\n\nSure, why not! Imagine: all that time you spend getting stuff to work for a few\nusers in crappy old browsers could instead be spent making awesome new features\nfor the vast majority of your users.\n\nFor inspiration, these brave websites have dropped the hammer and dropped\nsupport for IE:\n\n- GitHub (owned by Microsoft!)\n- devdocs.io\n- Flickr\n- Codepen\n","readmeFilename":"README.md","author":{"name":"David Gilbertson"},"homepage":"https://github.com/davidgilbertson/react-recollect#readme","repository":{"type":"git","url":"git+https://github.com/davidgilbertson/react-recollect.git"},"bugs":{"url":"https://github.com/davidgilbertson/react-recollect/issues"},"users":{"davidgilbertson":true}}