{"id":15745388,"url":"https://github.com/kouhin/redux-listener","last_synced_at":"2025-09-01T10:13:08.334Z","repository":{"id":57350964,"uuid":"110236890","full_name":"kouhin/redux-listener","owner":"kouhin","description":"Add/remove redux side effects dynamically","archived":false,"fork":false,"pushed_at":"2017-11-29T18:00:59.000Z","size":33,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-05T06:35:14.772Z","etag":null,"topics":["listener","middleware","redux","side-effects"],"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/kouhin.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-11-10T10:51:38.000Z","updated_at":"2017-11-29T18:03:25.000Z","dependencies_parsed_at":"2022-09-18T00:20:37.989Z","dependency_job_id":null,"html_url":"https://github.com/kouhin/redux-listener","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/kouhin/redux-listener","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kouhin%2Fredux-listener","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kouhin%2Fredux-listener/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kouhin%2Fredux-listener/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kouhin%2Fredux-listener/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kouhin","download_url":"https://codeload.github.com/kouhin/redux-listener/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kouhin%2Fredux-listener/sbom","scorecard":{"id":568485,"data":{"date":"2025-08-11","repo":{"name":"github.com/kouhin/redux-listener","commit":"6f21ef0e482c8fe01945afc7e121d6f7c76f86b1"},"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":"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":"Code-Review","score":0,"reason":"Found 0/1 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":"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":"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":"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":"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":"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":"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":"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":"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":"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":"Vulnerabilities","score":0,"reason":"54 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-cwfw-4gq5-mrqx","Warn: Project is vulnerable to: GHSA-g95f-p29q-9xw4","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-9vvw-cc9w-f27h","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-qrmc-fj45-qfc2","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-xf7w-r453-m56c","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-44pw-h2cw-w3vq","Warn: Project is vulnerable to: GHSA-jp4x-w63m-7wgm","Warn: Project is vulnerable to: GHSA-c429-5p7v-vgjp","Warn: Project is vulnerable to: GHSA-qqgx-2p2h-9c37","Warn: Project is vulnerable to: GHSA-2pr6-76vf-7546","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-9c47-m6qq-7p4h","Warn: Project is vulnerable to: GHSA-fvqr-27wr-82fm","Warn: Project is vulnerable to: GHSA-4xc9-xhrj-v574","Warn: Project is vulnerable to: GHSA-x5rq-j2xg-h7qm","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","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-g6ww-v8xp-vmwg","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-6g33-f262-xjp4","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-2m39-62fm-q8r3","Warn: Project is vulnerable to: GHSA-mf6x-7mm4-x2g7","Warn: Project is vulnerable to: GHSA-j44m-qm6p-hp7m","Warn: Project is vulnerable to: GHSA-3jfq-g458-7qm9","Warn: Project is vulnerable to: GHSA-5955-9wpr-37jh","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-g7q5-pjjr-gqvp","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-w5p7-h5w8-2hfq"],"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-20T15:39:20.167Z","repository_id":57350964,"created_at":"2025-08-20T15:39:20.167Z","updated_at":"2025-08-20T15:39:20.167Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273105959,"owners_count":25046950,"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-09-01T02:00:09.058Z","response_time":120,"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":["listener","middleware","redux","side-effects"],"created_at":"2024-10-04T04:03:18.120Z","updated_at":"2025-09-01T10:13:08.309Z","avatar_url":"https://github.com/kouhin.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-listener\n\nDispatch async action listener at any time and do side effect for Redux.\n\n## Installation\n\n``` bash\n$ npm install --save redux-listener\n```\n\n## Usage\n\n``` js\nimport { createStore, applyMiddleware } from 'redux';\nimport { createListenerMiddleware, on, attach } from 'redux-listener';\nimport rootReducer from './reducers';\n\nconst reduxListenerMiddleware = createReduxListenerMiddleware();\nconst store = createStore(\n  rootReducer,\n  applyMiddleware(createListenerMiddleware)\n);\n\nasync function someFunction() {\n  // 'on' is a plain action creator to help to add the listenr\n  store.dispatch(on('ASYNC_MESSAGE', (action, { dispatch }) =\u003e {\n    return new Promise(resolve =\u003e {\n      setTimeout(() =\u003e {\n        console.info('');\n        resolve();\n      }, 1000);\n    });\n  }));\n\n  store.dispatch({\n    type: 'INCREMENT_ASYNC',\n  });\n\n  const result = await attach(store.dispatch({\n  }))\n}\n```\n\n## Concepts\n\nThe basic concept of redux-listener is\n\n\u003e Do dispatch(async listener) for redux action at any time.\n\nTo archive this, there are serveral advanced concepts that are designed.\n\n### 1. Action listener is only for side effect, so **the original redux data flow shouldn't be interrupted**.\n\n// Chart\n\n### 2. Register async action listener **on demand** for **any plain redux action**.\n\nEazy to do code splitting. Just dispatch and add required async action listeners before business logic.\n\n### 3. For one action type, only one listener is allowed.\n\nEasy to make test. You can mock and override any exist listener to test.\n\nSo there are only on(type, listener) and off(type, listener), and no addListener and removeListener in order to avoid misunderstanding.\n\n**However multiple listeners can be composed as one listener for one action type.**\n\nThere is an example.\n\n``` js\nimport { query, on, attach } from 'redux-listener';\n\nasync function someFunction() {\n  const existListener = await attach(dispatch(query('SOME_ACTION_TYPE')));\n  dispatch(on('SOME_ACTION_TYPE', async (action, ...args) =\u003e {\n    // do something\n    const originalResult = await existListener(action, ...args);\n    // do something\n    return originalResult; // Or something else\n  }));\n}\n```\n\n### 4. The async listener should be waitable.\n\nFor chaining multiple async action.\n\n``` js\nasync function someFunction() {\n  await attach(dispatch({\n    type: \"ASYNC_FETCH_SCHOOL_REQUEST\",\n    payload: {\n      schoolId: 3\n    }\n  }));\n\n  const school = selectSchool(getState(), 3);\n  const classroomIds = school.classrooms;\n\n  const classroomPromises = classroomIds.map((id) =\u003e {\n    return attach(dispatch({\n      type: \"ASYNC_FETCH_CLASSROOM_\",\n      payload: {\n          classroomId: id\n      }\n    }));\n  });\n\n  await Promise.all(classroomPromises);\n  // ...\n}\n```\n\n### 5. dispatch(), getState() and other extra arguments are available as same as redux-thunk.\n\nBut take care of dispatching actions inside the async listener, it may cause infinite loop!!\n\n## INSTALLATION\n\nFor npm\n\n``` bash\nnpm install --save redux-listener\n```\n\nFor yarn\n\n``` bash\nyarn add redux-listener\n```\n\n## API\n\n### `createListenerMiddleware([extraArgument])`\n\nCreate a new listener middleware with extra argument.\n\n``` js\nconst store = createStore(\n  reducer,\n  applyMiddlware(createListenerMiddleware({ api, whatever })),\n);\n\nstore.dispatch(on('TYPE_OF_ACTION', async (action, dispatch, getState, { api, whatever }) =\u003e {\n}));\n```\n\n### `on(type, listener)`\n\nAdds an action listener for type. The value returned by listener can be accessed by `attach()` function.\n\n``` js\nstore.dispatch(on('TYPE_OF_ACTION', async (action, dispatch, getState, extraArgument) =\u003e {\n  // ...\n}));\n```\n\n#### Arguments\n\n- `type` ( _String_ ): Action type.\n- `listener(action, dispatch, getState, extraArgument): Promise or any` ( _Function_ ): Listener for specified action type. The `dispatched action`, `dispatch`, `getState`, `extraArgument` will be passed. Promise is recommended returned type, since the result can be accessed by `attach()`.\n\n### `off(type)`\n\nRemoves an action listen for type.\n\n``` js\nstore.dispatch(off('TYPE_OF_ACTION'));\n```\n\nAn action creator that is used to remove the listener for type.\n\n### `query(type)`\n\nAn action creator that is used to query the registered listener for type. Use `attach(result)` to access the returned listener.\n\n``` js\nasync function example() {\n  const result = store.dispatch(query('TYPE_OF_ACTION'));\n  const listener = await attach(result);\n}\n```\n\n**You can use this function to delegate the exist listener.**\n\n### `attach(dispatchResult)`\n\nAttachs the listener of dispatched action and gets the result.\n\n``` js\nasync function example() {\n  store.dispatch(on('ACTION_TYPE_A', async (action, dispatch, getState, extraArgument) =\u003e {\n    const { msg } = action.payload;\n    return msg;\n  }));\n\n  const result = store.dispatch({\n    type: 'ACTION_TYPE_A',\n    payload: {\n      msg: 'Hello'\n    }\n  });\n\n  const listenerResult = await attach(result);\n  console.info(listenerResult); // \"Hello\"\n}\n```\n\n## LICENSE\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkouhin%2Fredux-listener","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkouhin%2Fredux-listener","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkouhin%2Fredux-listener/lists"}