{"_id":"fselect","_rev":"2-94d2af904867670ccdd66c703cd5a66f","name":"fselect","dist-tags":{"latest":"1.0.31"},"versions":{"1.0.30":{"name":"fselect","version":"1.0.30","main":"dist/index.js","repository":{"type":"git","url":"git+ssh://git@github.com/foodlytech/FSelect.git"},"author":{"name":"Eric Kim","email":"bosung90@gmail.com"},"license":"MIT","babel":{"presets":["@babel/preset-env","@babel/preset-react"]},"keywords":["fselect","react","selector","select","redux"],"scripts":{"prebuild":"rimraf dist","build":"babel --out-dir dist --ignore **/*.test.js src","release":"release-it","test":"jest"},"dependencies":{"lodash":"^4.17.11","react":"16.4.1","react-redux":"^5.0.7","merge":">=1.2.1"},"devDependencies":{"@babel/cli":"^7.0.0","@babel/core":"^7.0.1","@babel/preset-env":"^7.0.0","@babel/preset-react":"^7.0.0","babel-core":"7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.6.0","enzyme-adapter-react-16":"^1.5.0","jest":"^23.6.0","react-dom":"^16.5.2","redux":"^4.0.0","redux-mock-store":"^1.5.3","release-it":"^7.6.1","rimraf":"^2.6.2"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"gitHead":"ca5faf4c89676bd2097aa4c60cc621bf5c1a58a2","description":"## Rethinking `connect`","bugs":{"url":"https://github.com/foodlytech/FSelect/issues"},"homepage":"https://github.com/foodlytech/FSelect#readme","_id":"fselect@1.0.30","_npmVersion":"6.1.0","_nodeVersion":"10.5.0","_npmUser":{"name":"bosung90","email":"bosung90@gmail.com"},"dist":{"integrity":"sha512-oBAVhYTaispeN7ni8u+9lpZIdQryZkoVm008p6LMS3h5l4wkIgnwlz07rohTUM56FVPJa9eNSryXw6DpbW3LnQ==","shasum":"65bcccf018a4fd76191876976f61f276985e35e4","tarball":"https://registry.npmjs.org/fselect/-/fselect-1.0.30.tgz","fileCount":4,"unpackedSize":24268,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJchGD4CRA9TVsSAnZWagAAmtkP/2+PLv+hFdbJhDqOJ1gf\nNOI+j3YuNPMxWszgOvJrU1fWkJoYZwg0HspevBveUpsEFs8EqQRLNgLlUFU8\njkQ3I88psw/CfH7EfEeAwEsxJowJzVxg4BGT1yDPNEbTyz9QHYUdZLnI0rTJ\nvdoa0d42yxvqWA6rO53vmtJJ88KHJmr8LI3Q90Cu/GYEY8PP/QMP0PS1sc/C\nyVU4+Y7i7zVCV8rN8zxSooOGLw0V2smhSqldzwSXh4a+j+3pvt95mzEE5sXT\ny+LphTskFoeOri6qNeohLQtviWHzfMehG0qazkdBtdyiXQDvC14bk7coUU9M\nAIaFjIIe6ITWUyyI6utIX+IXpr93Np8LuWRJWv2drfbakfy5E7doXtg1jYfx\nl6fiXz124tbZYI9IrMVRirt2m87QCZwadv22ZXu2U0lmOuZZhBpSlyzqUrzD\nkZnccwgrOj/ZMf2YFGVoNp/Lsi09ODUm1NDaF0GOPWCVFck2HtfeDy8+M/pX\nvaXfNY3+61yggZcQYuRZN0+Fwwi8M9dVWj96p9QNCOmZl0WKHGS/xJNWv/AZ\njSlZCfkmSUWfPvtdcMBuHE0JwtdQVQMZ1TG9SSneNusYz8I9KT/IbZEnmM5U\nxgBwBEa63JYcCJFGT+Uww39WqjY6DEjq3fxr6UB8qt88SO5bC4zLU7kNeQuW\nRh7a\r\n=DbmR\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEYCIQD3Djo5ud1h1sNa2Lz4CK2m6jS3racHYade/jXZ4lEdWwIhAOk5TWD7uDf2CmQrkKkUCelWSDDxl7rqjhFrgKrIeNhM"}]},"maintainers":[{"name":"bosung90","email":"bosung90@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fselect_1.0.30_1552179447143_0.30903415086868513"},"_hasShrinkwrap":false},"1.0.31":{"name":"fselect","version":"1.0.31","main":"dist/index.js","repository":{"type":"git","url":"git+ssh://git@github.com/foodlytech/FSelect.git"},"author":{"name":"Eric Kim","email":"bosung90@gmail.com"},"license":"MIT","babel":{"presets":["@babel/preset-env","@babel/preset-react"]},"keywords":["fselect","react","selector","select","redux"],"scripts":{"prebuild":"rimraf dist","build":"babel --out-dir dist --ignore **/*.test.js src","release":"release-it","test":"jest"},"dependencies":{"lodash":"^4.17.11","react":"16.4.1","react-redux":"^5.0.7","merge":">=1.2.1"},"devDependencies":{"@babel/cli":"^7.0.0","@babel/core":"^7.0.1","@babel/preset-env":"^7.0.0","@babel/preset-react":"^7.0.0","babel-core":"7.0.0-bridge.0","babel-jest":"^23.6.0","enzyme":"^3.6.0","enzyme-adapter-react-16":"^1.5.0","jest":"^23.6.0","react-dom":"^16.5.2","redux":"^4.0.0","redux-mock-store":"^1.5.3","release-it":"^7.6.1","rimraf":"^2.6.2"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"gitHead":"233c89c364a27a1fa5d0b1f85efcc0b2e9eaa633","description":"## Rethinking `connect`","bugs":{"url":"https://github.com/foodlytech/FSelect/issues"},"homepage":"https://github.com/foodlytech/FSelect#readme","_id":"fselect@1.0.31","_npmVersion":"6.1.0","_nodeVersion":"10.5.0","_npmUser":{"name":"bosung90","email":"bosung90@gmail.com"},"dist":{"integrity":"sha512-IQYrYgMyUux+WZZ5NgYk7Y88yOTit8ImBFPAQJxMiwg/F3TnHPirpl5zMvxjkhCgVxEMnZ4EyVMQyHHH7FAxVQ==","shasum":"ba40e862cd2fb61f24d78ff9dee6a546560a31ad","tarball":"https://registry.npmjs.org/fselect/-/fselect-1.0.31.tgz","fileCount":4,"unpackedSize":24264,"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v3.0.4\r\nComment: https://openpgpjs.org\r\n\r\nwsFcBAEBCAAQBQJcs+ewCRA9TVsSAnZWagAAwxEP/R7fz3boDnR0zAezVhAw\nVDwBHNRuZR1Y2169NG5xPDKgxFmvjQSreFRUwxatwEx41FVhhIdWrBUF1sdg\nq/GmyUNgZTUSYvvsS79QSiLgPOYkGdI9vxvJiAMbVPbEveZmR5C2axt1N2/+\nMsA+vn509tpBdrL2UrmF2eCLGAi3bGMJe0zyF7CHbFYKjqasehS7b9CgJI+H\n2in97yvgvis2SpzrdQipTW0yg5XvkEWRi+41662fMBD2f/FoVA+VkUd7qDD/\nRiLlHmzUfZmZzQM5gm0WjzGOhieRt+9llcg+9q5sCp4tVHArVFdrKt/ovStf\noiXRWndxd4+PF6nMHUfcVKNmEQygdUcAcBNVhMMMQszdQB7L1MAhxToDlQmI\neWIfVQmrfCjMD6wWRD+VmOTvASfXxkawmH+UM9tA85+dl0n+xeAEFkpawAxc\naYdzERB6S/LJpx9ZxBhggeANkRFA5YQLV1DQUhTSVOxdJbEucn/XF4C5XwUA\n2BC9KwV7G1gMlSC4pcaQkrlbNUWvYq9suhL0YcoAVZZTajN6GHQKVvmmAe9b\nRwPOLEaZhuM78nW3UPb2jJnvqOBdh+WC9JVLqRnETvaL4Toc9jJtyBnt5D5y\n+YZTmB/u4nKoeBZd1UGzDtwtftQNiU+0Jf2CqFAM+lJc3O8wrYhbKiw+qoVF\nwct8\r\n=oytn\r\n-----END PGP SIGNATURE-----\r\n","signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIBAZEDwHHZVJfWCUEIeZzM3jvOkhex+j3CzphRjLp0W/AiEArU8+VAsBhBKbqtK/seCtNXT/Tziv+CIxH/wgcaubJlA="}]},"maintainers":[{"name":"bosung90","email":"bosung90@gmail.com"}],"directories":{},"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/fselect_1.0.31_1555294127769_0.0705293985314217"},"_hasShrinkwrap":false}},"time":{"created":"2019-03-10T00:57:27.143Z","1.0.30":"2019-03-10T00:57:27.357Z","modified":"2022-05-02T23:44:32.596Z","1.0.31":"2019-04-15T02:08:48.090Z"},"maintainers":[{"name":"bosung90","email":"bosung90@gmail.com"}],"description":"## Rethinking `connect`","homepage":"https://github.com/foodlytech/FSelect#readme","keywords":["fselect","react","selector","select","redux"],"repository":{"type":"git","url":"git+ssh://git@github.com/foodlytech/FSelect.git"},"author":{"name":"Eric Kim","email":"bosung90@gmail.com"},"bugs":{"url":"https://github.com/foodlytech/FSelect/issues"},"license":"MIT","readme":"# FSelect [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=Stop%20using%20Redux%20connect.%20Use%20FSelect.&url=https://github.com/foodlytech/FSelect&hashtags=fselect,selector,redux,connect,react-redux,react,react-native)\n\n## Rethinking `connect`\n\nFSelect abstracts away react-redux's `connect` using the power of render props. By using FSelect instead of `connect` your code will be cleaner and faster.\n\nThis library is already being used in production at Foodly and works with both ReactDOM and React Native.\n\n## Index\n\n- [Getting Started](#getting-started)\n- [Replacing Connect](#replacing-connect)\n- [Conditional Element Render](#conditional-element-render)\n- [Avoiding Rerender](#avoiding-rerender)\n- [Takeaways](#takeaways)\n- [Future Roadmap](#future-roadmap)\n\n## Getting Started\n\n```sh\nnpm i -S fselect\n```\n\nor\n\n```sh\nyarn add fselect\n```\n\n## Replacing Connect\n\nBefore\n\n```jsx\nimport React from 'react'\nimport { connect } from 'react-redux'\n\nclass App extends React.Component {\n  render() {\n    return <span>Welcome {this.props.email}</span>\n  }\n}\n\nconnect(state => ({ email: state.login.email }))(App)\n```\n\nUsing FSelect\n\n```jsx\nimport React from 'react'\nimport FSelect from 'fselect'\n\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect selector={state => state.login.email}>\n        {email => <span>Welcome {email}</span>}\n      </FSelect>\n    )\n  }\n}\n```\n\nEquivalently you can also write using self closing tag\n\n```jsx\nimport React from 'react'\nimport FSelect from 'fselect'\n\nclass App extends React.Component {\n  render() {\n    return (\n      <span>\n        Welcome <FSelect selector={state => state.login.email} />\n      </span>\n    )\n  }\n}\n```\n\nMultiple states from Redux using object\n\n```jsx\nimport React from 'react'\nimport FSelect from 'fselect'\n\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect\n        selector={{\n          email: state => state.login.email,\n          name: state => state.login.name,\n        }}\n      >\n        {({ email, name }) => (\n          <Text>\n            Welcome {name} {email}\n          </Text>\n        )}\n      </FSelect>\n    )\n  }\n}\n```\n\nMultiple states from Redux using array\n\n```jsx\nimport React from 'react'\nimport FSelect from 'fselect'\n\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect\n        selector={[state => state.login.email, state => state.login.name]}\n      >\n        {([email, name]) => (\n          <Text>\n            Welcome {name} {email}\n          </Text>\n        )}\n      </FSelect>\n    )\n  }\n}\n```\n\nEquivalently you can just use two self-closing tag\n\n```jsx\nimport React from 'react'\nimport FSelect from 'fselect'\n\nclass App extends React.Component {\n  render() {\n    return (\n      <span>\n        Welcome\n        <FSelect selector={state => state.login.name} />\n        <FSelect selector={state => state.login.email} />\n      </span>\n    )\n  }\n}\n```\n\nUsing selectors\n\n```jsx\nimport * as selectors from './selectors'\n ...\nreturn (\n  <FSelect\n    selector={{\n      email: state => selectors.getEmail(state),\n      name: state => selectors.getName(state),\n    }}\n  >\n    {({ email, name }) => (\n      <Text>\n        Welcome {name} {email}\n      </Text>\n    )}\n  </FSelect>\n)\n```\n\n## What about `dispatch`?\n\nThere is a huge misconception in react community that dispatch is only available through using connect. However, if you know that you will never support server-sided rendering we recommend importing dispatch directly.\n\nWe still recommend using action creators rather than using dispatch directly in your UI Component.\n\nIf you need to support server-side rendering you will have to use connect to have access to dispatch, until we support optional withDispatch props.\n\nBefore\n\n```jsx\nclass A extends React.Component {\n  render() {\n    return (\n      <button onClick={()=> this.props.dispatch(...)} />\n    )\n  }\n}\n\nconnect()(A)\n```\n\nAfter\n\n```jsx\n// src/store.js\nimport { createStore } from 'redux'\nconst store = createStore(...)\nconst { dispatch, getState } = store\nexport { dispatch, getState }\n\n// App.js\nimport { dispatch } from 'src/store'\n\nclass A extends React.Component {\n  render() {\n    return (\n      <button onClick={()=> dispatch(...)} />\n    )\n  }\n}\n```\n\n## I need access to redux state during some event (ie. button press)\n\nAgain, there is a huge misconception in react community that you need connect or in this case FSelect to have access to redux state during some event.\n\nUnless you need to support server-sided rendering, you can import getState directly from the store.\n\nBefore\n\n```jsx\nclass App extends React.Component {\n  render() {\n    return <button onClick={this.handleButton}>Welcome</button>\n  }\n  handleButton = () => {\n    actions.uploadToServer(this.props.email)\n  }\n}\n\nconnect(state => ({ email: state.login.email }))(App)\n```\n\nAfter\n\n```jsx\n// src/store.js\nimport { createStore } from 'redux'\nconst store = createStore(...)\nconst { dispatch, getState } = store\nexport { dispatch, getState }\n\n// App.js\nimport { getState } from 'src/store'\n\nclass App extends React.Component {\n  render() {\n    return <button onClick={this.handleButton}>Welcome</button>\n  }\n  handleButton = () => {\n    actions.uploadToServer(getState().login.email)\n  }\n}\n```\n\nYou do NOT need connect or FSelect unless your UI depends on the redux state.\n\nSimilarily, we recommend using selectors so that your UI component isn't coupled to the structure of your redux store.\n\nJust as an example\n\n```jsx\n// actions.js\nimport {dispatch, getState} from 'src/store'\n\nexport const uploadEmailToServer = () {\n  // I recommend using selector instead of state.login.email\n  const email = getState().login.email\n  api.uploadEmailToServer(email)\n}\n\n// App.js\nimport * as actions from 'src/actions'\n\nclass App {\n  ...\n  handleButton = () => {\n    actions.uploadEmailToServer()\n  }\n}\n```\n\nInstead of using pure function selectors (which requires root state as parameter), I recommend it having access to state directly and creating actions that returns part of the reduxState.\n\n```jsx\nimport {dispatch, getState} from 'src/store'\n\nexport const getEmail = () => {\n  return getState().login.email\n}\n\nexport const uploadEmailToServer = () {\n  return api.uploadEmailToServer(getEmail())\n}\n```\n\n## Conditional Element Render\n\nIn React, we often want to render a React Element if and only if certain condition is met. Most often than not we use inline conditional render using `&&`\n\n```jsx\nrender() {\n  return (\n    <div>\n      {this.state.isLoggedIn && (\n        <span>Welcome XXX</span>\n      )}\n    </div>\n  )\n}\n```\n\nHowever, we soon realize things get a little messy as we write more things\n\n```jsx\n// Error: cannot render undefined\nstate = {email: undefined}\nrender() {\n  return (\n    <div>\n      {this.state.email && (\n        <span>Welcome {this.state.email}</span>\n      )}\n    </div>\n  )\n}\n```\n\n```jsx\n// Why are we rendering empty string?\nstate = {email: ''}\nrender() {\n  return (\n    <div>\n      {this.state.email && (\n        <span>Welcome {this.state.email}</span>\n      )}\n    </div>\n  )\n}\n```\n\n```jsx\n// Oh wait, we can't have multiple React Elements\nrender() {\n  return (\n    <div>\n      {this.state.isLoggedIn && (\n        <span>Welcome XXX</span>\n        <span>You rock!</span>\n      )}\n    </div>\n  )\n}\n\n// This is horrible\nrender() {\n  return (\n    <div>\n      {this.state.isLoggedIn && (\n        <span>Welcome XXX</span>\n      )}\n      {this.state.isLoggedIn && (\n        <span>You rock!</span>\n      )}\n    </div>\n  )\n}\n\n// This is even worse\nrender() {\n  return (\n    <div>\n      {this.state.isLoggedIn && (\n        [<span key={0}>Welcome XXX</span>,\n        <span key={1}>You rock!</span>]\n      )}\n    </div>\n  )\n}\n\n// Damn now I have to fix styling\nrender() {\n  return (\n    <div>\n      {this.state.isLoggedIn && (\n        <div>\n          <span>Welcome XXX</span>\n          <span>You rock!</span>\n        </div>\n      )}\n    </div>\n  )\n}\n```\n\nUsing FSelect, you can also do Conditional Element Render. Your element will only render if selector value is truthy\n\n```jsx\nrender() {\n  return (\n    <FSelect selector={this.state.isLoggedIn}>\n      <span>Welcome XXX</span>\n      <span>You rock!</span>\n    </FSelect>\n  )\n}\n```\n\nAnd yes, your condition can depend on redux state as well\n\n```jsx\nrender() {\n  return (\n    <FSelect selector={state => state.login.isLoggedIn}>\n      <span>Welcome XXX</span>\n      <span>You rock!</span>\n    </FSelect>\n  )\n}\n```\n\nYou can also do Conditional Element Render based on multiple states using array or object.\n\nYour Element will only render if all of values in array or object evaluates to truthy (and yes it can be a function with Redux state)\n\n```jsx\nrender() {\n  return (\n    <FSelect\n      selector={\n        [state => state.login.isLoggedIn, state => !state.login.isBanned]}>\n      <span>Welcome XXX</span>\n      <span>You rock!</span>\n    </FSelect>\n  )\n}\n```\n\nTo Conditionally Render with falsy values, use `selectorNot` props. Like selector, selectorNot will accept true, false, array, and objects.\n\n```jsx\nrender() {\n  return (\n    <FSelect\n    selectorNot={state => state.login.isBanned}\n    selector={state => state.login.isLoggedIn}>\n      <span>Welcome XXX</span>\n      <span>You rock!</span>\n    </FSelect>\n  )\n}\n```\n\nWARNING: If your Conditional Element Render depends on the redux state, you must pass a function as selector or selectorNot (or function as part of array or object values). Failing to do so will cause your FSelect to not properly re-render the child component unless its parent Component re-renders.\n\nFor instance: DO NOT DO\n\n```jsx\nrender() {\n  return (\n    <FSelect\n    // Do not do this, your child component will not re-render unless parent component of FSelect re-renders\n    selector={getState().login.isLoggedIn}>\n      <span>Welcome XXX</span>\n      <span>You rock!</span>\n    </FSelect>\n  )\n}\n```\n\nAs a fallback, not providing selector props will render props with root state\n\n```jsx\nreturn <FSelect>{state => <span>Welcome {state.login.email}</span>}</FSelect>\n```\n\n## Avoiding Rerender\n\nFSelect already optimizes your code such that re-render is localized to the specific component rather than entire component that you have connected.\n\nHowever, to further optimize and avoid unneccesary creation of arrow functions and re-renders when the parent component re-renders, you can use popular method that React community uses\n\n```jsx\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect selector={state => state.login.email}>\n        {this.renderEmailText}\n      </FSelect>\n    )\n  }\n  renderEmailText = email => {\n    return <span>Welcome {email}</span>\n  }\n}\n```\n\nNote that using above method will only re-render EmailText when email changes, therefore if your emailText depends on any other state or prop, you must pass that as argument through selector prop.\n\n```jsx\n// Don't do this, your EmailText won't re-render when only this.state.name changes.\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect selector={state => state.login.email}>\n        {this.renderEmailText}\n      </FSelect>\n    )\n  }\n  renderEmailText = email => {\n    return (\n      <span>\n        Welcome {this.state.name} {email}\n      </span>\n    )\n  }\n}\n// Do this instead\nclass App extends React.Component {\n  render() {\n    return (\n      <FSelect\n        selector={state => ({\n          name: this.state.name,\n          email: state.login.email,\n        })}\n      >\n        {this.renderEmailText}\n      </FSelect>\n    )\n  }\n  renderEmailText = ({ name, email }) => {\n    return (\n      <span>\n        Welcome {name} {email}\n      </span>\n    )\n  }\n}\n```\n\n### What does F stand for?\n\nF stands for Foodly. We create online ordering platform for restaurants.\nPlease visit https://foodly.tech for more information.\n\n## Takeaways\n\n1. Import dispatch from the store\n2. Use getState from the store for state dependent event, or use actions with getState built in.\n3. Use FSelect for UI that depends on the redux state.\n4. Conditional Element Render using FSelect\n\n## Future Roadmap\n\n- [x] Conditional Element Render selectorNot props\n- [ ] Conditional Element Render selectorOr props\n- [ ] Conditional Element Render selectorNotOr props\n- [ ] Allow context to be passed as props to override state from context rather than react-redux's Provider\n- [ ] Access to dispatch with optional withDispatch props\n- [ ] Self Closing tag supporting array of values\n- [ ] Remove lodash dependency\n- [ ] Performance tests\n- [x] Unit tests\n- [ ] CI running tests and releases\n- [ ] Types (FlowTypes, TypeScript, PropTypes)\n- [ ] Example project (React Web, React Native)\n\n## Breaking changes proposal for 2.0\n\n`Conditional Element Render` will no longer work with `selector` props and will be replaced with `ifSo` props. This allows both selector and ifSo to be used in conjunction. `selectorNot` will be replaced with `ifNot`. `ifSoOr` and `ifNotOr` will also be added.\n\nPlease help with any of the above future maps, bugs, and spelling mistakes.\nAny PR or feedbacks are welcome.\n\nThank you!\n","readmeFilename":"README.md"}