{"id":24812164,"url":"https://github.com/sibnerian/selector-action","last_synced_at":"2025-10-13T13:31:55.059Z","repository":{"id":48032794,"uuid":"96064819","full_name":"sibnerian/selector-action","owner":"sibnerian","description":"State-aware Redux actions with Reselect syntax.","archived":false,"fork":false,"pushed_at":"2023-01-03T15:15:56.000Z","size":885,"stargazers_count":12,"open_issues_count":10,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-17T07:56:22.527Z","etag":null,"topics":["middleware","redux","redux-thunk","reselect","selector","selector-action"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sibnerian.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-07-03T02:57:22.000Z","updated_at":"2023-03-04T05:37:49.000Z","dependencies_parsed_at":"2023-02-01T07:01:04.492Z","dependency_job_id":null,"html_url":"https://github.com/sibnerian/selector-action","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/sibnerian/selector-action","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sibnerian%2Fselector-action","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sibnerian%2Fselector-action/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sibnerian%2Fselector-action/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sibnerian%2Fselector-action/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sibnerian","download_url":"https://codeload.github.com/sibnerian/selector-action/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sibnerian%2Fselector-action/sbom","scorecard":{"id":822192,"data":{"date":"2025-08-11","repo":{"name":"github.com/sibnerian/selector-action","commit":"b5a98e8c6c38dce530b91c3b8eba7eab091574f2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":0,"reason":"Found 0/20 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 11 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"44 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-w573-4hg7-7wgq","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-8r6j-v8pm-fqw3","Warn: Project is vulnerable to: MAL-2023-462","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-43f8-2h32-f4cj","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-6c8f-qphg-qjgp","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-4g88-fppr-53pp","Warn: Project is vulnerable to: GHSA-4jqc-8m5r-9rpr","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-r628-mhmh-qjhw","Warn: Project is vulnerable to: GHSA-9r2w-394v-53qc","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-qq89-hq3f-393p","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-c4w7-xm78-47vh","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T15:52:42.659Z","repository_id":48032794,"created_at":"2025-08-23T15:52:42.660Z","updated_at":"2025-08-23T15:52:42.660Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279015312,"owners_count":26085684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["middleware","redux","redux-thunk","reselect","selector","selector-action"],"created_at":"2025-01-30T13:18:26.200Z","updated_at":"2025-10-13T13:31:54.695Z","avatar_url":"https://github.com/sibnerian.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# State-aware Redux actions with Reselect syntax\n\n[![Build Status](https://travis-ci.org/sibnerian/selector-action.svg?branch=master)](https://travis-ci.org/sibnerian/selector-action) [![Coverage Status](https://coveralls.io/repos/github/sibnerian/selector-action/badge.svg?branch=master)](https://coveralls.io/github/sibnerian/selector-action?branch=master) [![npm version](https://badge.fury.io/js/selector-action.svg?branch=master)](https://badge.fury.io/js/selector-action)\n\n### Basic Usage\n\n```js\nimport selectorAction from 'selector-action';\n\n// ...\n\nexport const reloadActiveItem = selectorAction(\n  activeIdSelector,\n  (activeId) =\u003e ({\n    type: 'RELOAD_ACTIVE_ITEM',\n    promise: fetch(`//website.com/items/${activeId}`),\n  }),\n);\n```\n\n### Background\n\n**selector-action** simplifies a common Redux pattern: actions that depend on the\ncurrent Redux state. For example, when you're reloading an \"active\" item's data with an API call:\n\n```js\n// The active ID is in the Redux state, but we need it to make our API call. Sad!\nexport function reloadActiveItem(activeId) {\n  return {\n    type: 'RELOAD_ACTIVE_ITEM',\n    promise: fetch(`//website.com/items/${activeId}`),\n  };\n}\n```\n\nIn this example, we have to pass `activeId` to the action creator, even though it’s actually\npart of the Redux state! This pollutes our React components with unnecessary props, and makes\nthe action creators more complicated than they need to be. We could try using [`redux-thunk`](https://github.com/gaearon/redux-thunk) to eliminat the argument, but that adds a lot of boilerplate:\n\n```js\nexport function reloadActiveItem() {\n  // We can access the current state by returning a thunk.\n  return (dispatch, getState) =\u003e {\n    const state = getState();\n    const activeId = activeIdSelector(state);\n    // Now we just have to dispatch the action...\n    return dispatch({\n      type: 'RELOAD_ACTIVE_ITEM',\n      promise: fetch(`//website.com/items/${activeId}`),\n    });\n  };\n}\n```\n\nWe have to get the state, call selectors on it, and finally `dispatch` the resulting action.\nAnd if we wanted to test this action creator, there's even _more_ boilerplate - we'd have to mock\n`getState`, then spy on the `dispatch` function...it's not pretty.\n\n## Using `selector-action`\n\n`selectorAction` makes this pattern a breeze. Instead of doing the dispatching\nand the action-creating ourselves, we’ll pass in a selector and an action creator function that uses\nthe selector's results. The end result is a `reloadActiveItem` function that's exactly equivalent\nto the previous example.\n\n```js\nimport selectorAction from 'selector-action';\n\n// ...\n\nexport const reloadActiveItem = selectorAction(\n  activeIdSelector,\n  (activeId) =\u003e ({\n    type: 'RELOAD_ACTIVE_ITEM',\n    promise: fetch(`//website.com/items/${activeId}`),\n  }),\n);\n```\n\nAs you can see, this looks a lot like the syntax of [Reselect](https://github.com/reactjs/reselect).\nYou pass in one or more selectors, then an action creator that takes the selectors’ return values as\nits arguments. The result of this action creator function is what's dispatched.\n\n### Testing\n\n`selectorAction` exposes the original action creator as an\n`originalActionCreator` property of the generated function. This allows for testing the\nunderlying action creator without using a fake state or stubbing `dispatch`.\n\n```js\ndescribe('reloadActiveItem', () =\u003e {\n  it('returns an action with the correct type', () =\u003e {\n    const activeId = 1234;\n    const action = reloadActiveItem.originalActionCreator(activeId);\n    expect(action.type).to.eql('RELOAD_ACTIVE_ITEM');\n  });\n});\n```\n\n## middleware\n\nIf you're already using [`redux-thunk`](https://github.com/gaearon/redux-thunk), you don't need to\ndo anything to start using `selectorAction` - it’s fully compatible with the thunk middleware.\n\nIf you don’t _want_ to use `redux-thunk` (and there are some\n[good](https://twitter.com/intelligibabble/status/800103510624727040)\n[reasons](http://blog.isquaredsoftware.com/2017/01/idiomatic-redux-thoughts-on-thunks-sagas-abstraction-and-reusability/)\nto not want to\n), then `selector-action` provides a middleware for you to use instead:\n\n```js\nimport { createStore, applyMiddleware } from 'redux';\nimport selectorActionMiddleware from 'selector-action/middleware';\nimport rootReducer from './reducers/index';\n\nconst store = createStore(\n  rootReducer,\n  applyMiddleware(selectorActionMiddleware),\n);\n```\n\n`selector-action`'s middleware only runs when `selectorAction`s are dispatched. Similar to\n`redux-thunk`, it plays nice with other common middlewares like\n[`redux-pack`](https://github.com/lelandrichardson/redux-pack).\n\n### Advanced features\n\n#### `state` as an argument\n\nIn addition to the results of the selectors, `state` is passed in as the last argument to the action\ncreator. If you don't specify any selectors, then `state` is the _only_ argument. This can be useful\nfor converting actions that already use the `getState` trick shown above.\n\n```js\nexport const reloadActiveItem = selectorAction((state) =\u003e {\n  const activeId = activeIdSelector(state);\n  return {\n    type: 'RELOAD_ACTIVE_ITEM',\n    promise: fetch(`//website.com/items/${activeId}`),\n  };\n});\n```\n\n#### Arrays of selectors\n\nLike Reselect, you can pass in an array of selectors instead of passing them as separate arguments.\nHere is a contrived example demonstrating this feature.\n\n```js\nexport const awesomeAction = selectorAction(\n  [fooSelector, barSelector],\n  (foo, bar) =\u003e ({ type: 'AWESOME!', payload: { foo, bar } }),\n);\n```\n\n#### `selectorAction` with arguments\n\nYou'll probably run across a case where an action creator needs a _mix_ of selector results and\nregular arguments to compute an action. For instance, let’s say that the user has entered a new name\nfor the current active item. Your reducer will need both the new name AND the active item’s ID\nto compute a new state. This can be done by wrapping `selectorAction` in a higher-order function.\n\n```js\nexport function setActiveItemName(newName) {\n  return selectorAction(activeIdSelector, (activeId) =\u003e ({\n    type: 'SET_ITEM_NAME',\n    payload: { activeId, newName },\n  }));\n}\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsibnerian%2Fselector-action","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsibnerian%2Fselector-action","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsibnerian%2Fselector-action/lists"}