{"id":15753576,"url":"https://github.com/marella/redux-reflex","last_synced_at":"2026-05-08T06:48:15.888Z","repository":{"id":57351349,"uuid":"97861597","full_name":"marella/redux-reflex","owner":"marella","description":"Reduce boilerplate code by automatically creating action creators and action types from reducers.","archived":false,"fork":false,"pushed_at":"2021-06-08T18:07:07.000Z","size":15,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-10T08:08:33.602Z","etag":null,"topics":["actions","boilerplate","reducers","redux","reflex"],"latest_commit_sha":null,"homepage":null,"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/marella.png","metadata":{"files":{"readme":"README.md","changelog":null,"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-20T17:29:30.000Z","updated_at":"2024-02-14T02:07:38.000Z","dependencies_parsed_at":"2022-08-31T03:53:18.317Z","dependency_job_id":null,"html_url":"https://github.com/marella/redux-reflex","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/marella/redux-reflex","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marella%2Fredux-reflex","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marella%2Fredux-reflex/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marella%2Fredux-reflex/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marella%2Fredux-reflex/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marella","download_url":"https://codeload.github.com/marella/redux-reflex/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marella%2Fredux-reflex/sbom","scorecard":{"id":618806,"data":{"date":"2025-08-11","repo":{"name":"github.com/marella/redux-reflex","commit":"28007b4050472043e4e33748cfaf1c9782b0b8e0"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"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":"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":"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":"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":"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/21 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":"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":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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"}}]},"last_synced_at":"2025-08-21T04:42:50.014Z","repository_id":57351349,"created_at":"2025-08-21T04:42:50.014Z","updated_at":"2025-08-21T04:42:50.014Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003294,"owners_count":26083555,"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-10T02:00:06.843Z","response_time":62,"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":["actions","boilerplate","reducers","redux","reflex"],"created_at":"2024-10-04T07:41:16.210Z","updated_at":"2025-10-10T08:08:36.437Z","avatar_url":"https://github.com/marella.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-reflex\n\nReduce boilerplate code by automatically creating action creators and action types from reducers.\n\n[![Build Status](https://travis-ci.org/marella/redux-reflex.svg?branch=master)](https://travis-ci.org/marella/redux-reflex) [![Coverage Status](https://coveralls.io/repos/github/marella/redux-reflex/badge.svg)](https://coveralls.io/github/marella/redux-reflex)\n\n\u003c!-- TOC depthFrom:2 depthTo:3 withLinks:1 updateOnSave:1 orderedList:0 --\u003e\n\n- [Installation](#installation)\n- [Example](#example)\n- [Documentation](#documentation)\n\t- [Reducer](#reducer)\n\t- [Actions](#actions)\n\t- [Async Actions](#async-actions)\n\t- [Reusing Reducer Logic](#reusing-reducer-logic)\n- [License](#license)\n\n\u003c!-- /TOC --\u003e\n\n\n## Installation\n\n```bash\nnpm install redux-reflex --save\n```\n\n\n## Example\n\n*reducer.js*\n\n```js\nimport Reducer from 'redux-reflex'\n\nconst prefix = 'counter'\nconst initialState = { count: 0 }\nconst handlers = {\n  // called when action type is 'counter/increment'\n  increment(state, amount) {\n    return { ...state, count: state.count + amount }\n  },\n  // called when action type is 'counter/decrement'\n  decrement(state, amount) {\n    return { ...state, count: state.count - amount }\n  },\n}\n\nconst counter = Reducer(prefix, initialState, handlers)\n\nexport default counter\n```\n\nThis will create a reducer function `counter` with `initialState` as the initial state and `handlers` as case reducers. `handlers` handle actions with type starting with `prefix`.\n\nYou don't have to define action creators or action types. They are automatically created and attached to the reducer function:\n\n```js\ncounter.increment // action creator (function)\n\ncounter.increment.type // action type (string) - 'counter/increment'\n\ncounter.increment() // creates action (object) - { type: 'counter/increment' }\n\ndispatch(counter.increment(5)) // increases count by 5\n```\n\n\n## Documentation\n\nGenerated actions are similar to [Flux Standard Action].\n\n```js\ncounter.increment()\n/*\n{\n  type: 'counter/increment',\n}\n*/\n\ncounter.increment(5)\n/*\n{\n  type: 'counter/increment',\n  payload: 5,\n}\n*/\n```\n\nIf payload is an instance of an [`Error`][Error] object then `error` is automatically set to `true`.\n\n```js\ncounter.increment(new Error())\n/*\n{\n  type: 'counter/increment',\n  payload: new Error(),\n  error: true,\n}\n*/\n```\n\n### Reducer\n\nThe main `Reducer` function creates a reducer and corresponding action creators and types.\n\n```js\nimport Reducer from 'redux-reflex'\n\nconst todos = Reducer(...)\n```\n\n#### `Reducer(prefix, initialState, handlers, options = {})`\n\n##### `prefix`\n\n`prefix` should be unique as `prefix + '/'` is used as a prefix for the action types that are automatically created. You can follow this convention to keep them unique: `\u003capp-name\u003e/\u003cfeature-name\u003e/\u003creducer-name\u003e`.\n\n##### `initialState`\n\nInitial value of the state managed by reducer.\n\n##### `handlers`\n\nAction `handlers` let you split the reducer code into smaller functions instead of using `switch-case` statements. These are also called \"case reducers\".\n\n###### `handler(state, payload, action)`\n\nEach `handler` should be a pure function that reads current state, payload and action and returns a new state: `(state, payload, action) =\u003e state`. A typical reducer function looks like `(state, action) =\u003e state` but here `payload` is passed as the second argument as it will be required most of the time. If you need more info about an action, you can always use the third argument `action` which contains action type, payload and other data related to action. For example, to check if an action is dispatched because of an error:\n\n```js\n// some action handler\nfetched(state, payload, { error }) {\n  // state should not be modified directly\n  // so do a shallow copy first\n  state = { ...state }\n\n  // state updates common to both success and failure cases\n  state.fetching = false\n\n  // failure case\n  if (error) {\n    state.error = payload\n    return state\n  }\n\n  // success case\n  state.data = payload\n  return state\n}\n```\n\nHere we used [Destructuring assignment] to unpack `error` from the third argument `action`.\n\n##### `options`\n\n`options` object is used for configuring the `Reducer`. Currently these options are available:\n\n```js\n{\n  copy: false,\n  // when `true`, `Reducer` automatically does a shallow copy of `state`\n  // before calling a handler function\n}\n```\n\n```js\nimport Reducer from 'redux-reflex'\n\nconst todos = Reducer(\n  'todos',\n  { todos: [] },\n  {\n    // this function will be called when action type is 'todos/add'\n    add(state, { text }) {\n      // Since `copy` option is set to `true`, `Reducer` automatically\n      // does a shallow copy of `state` before calling this function.\n      // So you don't have to do `{ ...state }`.\n\n      // `concat()` returns a new copy of array\n      // and doesn't modify the original one.\n      // `push()` should not be used as it modifies\n      // the original array.\n      state.todos = state.todos.concat(text)\n\n      return state\n    },\n  },\n  { copy: true }\n)\n```\n\nHere we used [Destructuring assignment] to unpack `text` from `payload`. If `payload` is not passed as second argument then unpacking complex objects becomes hard to read.\n\n### Actions\n\nAction creators are automatically created from reducer functions. If you need to define more action creators, you can use the `Action` function:\n\n```js\nimport { Action } from 'redux-reflex'\n\ntodos.add = Action('todos/add')\n\ntodos.add({ text: 'some task' })\n/*\n{\n  type: 'todos/add',\n  payload: { text: 'some task' },\n}\n*/\n```\n\n#### `Action(type, transform = payload =\u003e payload)`\n\n##### `type`\n\n`type` specifies the action type and should be unique. You can follow this convention to keep it unique: `\u003capp-name\u003e/\u003cfeature-name\u003e/\u003creducer-name\u003e/\u003caction-name\u003e`.\n\n##### `transform`\n\n`transform` is a function that can be used to modify the payload:\n\n```js\nimport { Action } from 'redux-reflex'\n\ntodos.add = Action('todos/add', payload =\u003e ({ text: payload }))\n\ntodos.add('some task')\n/*\n{\n  type: 'todos/add',\n  payload: { text: 'some task' },\n}\n*/\n```\n\n#### `transform(actionCreator, transform)`\n\nApply a transform function to existing action creators:\n\n```js\nimport Reducer, { transform } from 'redux-reflex'\n\nconst todos = Reducer(...) // assume that todos.add is already defined here\n\ntodos.add('some task')\n/*\n{\n  type: 'todos/add',\n  payload: 'some task',\n}\n*/\n\ntodos.add = transform(todos.add, payload =\u003e ({ text: payload }))\n\ntodos.add('some task')\n/*\n{\n  type: 'todos/add',\n  payload: { text: 'some task' },\n}\n*/\n```\n\n### Async Actions\n\nAsync actions can be handled using a middleware like [`redux-thunk`][redux-thunk]:\n\n```js\n// define `todos.fetching` and `todos.fetched` using `Reducer` function\n// and use the `error` property inside `todos.fetched` handler function\n// to handle both success and failure cases\ntodos.fetch = () =\u003e dispatch =\u003e {\n  dispatch(todos.fetching())\n  return api\n    .get('/todos')\n    .then(response =\u003e dispatch(todos.fetched(response.data)))\n    .catch(error =\u003e dispatch(todos.fetched(error)))\n}\n\n// dispatch it like a normal action\ndispatch(todos.fetch())\n```\n\n### Reusing Reducer Logic\n\nLet's say you want to have two counters both having their own state but with same functionality. To do so, you just have to define two reducers with same initial state and handlers:\n\n```js\nimport { combineReducers, createStore } from 'redux'\nimport Reducer from 'redux-reflex'\n\nconst initialState = { count: 0 }\nconst handlers = {\n  increment(state, amount) {\n    return { ...state, count: state.count + amount }\n  },\n  decrement(state, amount) {\n    return { ...state, count: state.count - amount }\n  },\n}\n\nconst counter1 = Reducer('counter1', initialState, handlers)\nconst counter2 = Reducer('counter2', initialState, handlers)\n\nconst store = createStore(combineReducers({ counter1, counter2 }))\n```\n\nHere `counter1` and `counter2` have their own slice of state and calling an action of one counter doesn't affect the state of other.\n\n```js\ncounter1.increment() // handled by counter1 reducer\n/*\n{\n  type: 'counter1/increment',\n}\n*/\n\ncounter2.increment() // handled by counter2 reducer\n/*\n{\n  type: 'counter2/increment',\n}\n*/\n```\n\n## License\n\n[MIT][license]\n\n\n[license]: /LICENSE\n[Flux Standard Action]: https://github.com/acdlite/flux-standard-action\n[redux-thunk]: https://github.com/gaearon/redux-thunk\n[Error]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error\n[Destructuring assignment]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarella%2Fredux-reflex","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarella%2Fredux-reflex","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarella%2Fredux-reflex/lists"}