{"id":21429443,"url":"https://github.com/alonrbar/redux-app","last_synced_at":"2026-04-19T19:02:25.143Z","repository":{"id":57350214,"uuid":"107044104","full_name":"alonrbar/redux-app","owner":"alonrbar","description":"Type-safe, DRY and OO redux. Implemented with typescript.","archived":false,"fork":false,"pushed_at":"2018-05-13T16:32:52.000Z","size":741,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-14T01:13:16.014Z","etag":null,"topics":["angular","dry","oop","react","redux","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/alonrbar.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-15T19:51:16.000Z","updated_at":"2018-09-04T06:42:42.000Z","dependencies_parsed_at":"2022-08-28T20:21:42.465Z","dependency_job_id":null,"html_url":"https://github.com/alonrbar/redux-app","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/alonrbar/redux-app","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alonrbar%2Fredux-app","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alonrbar%2Fredux-app/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alonrbar%2Fredux-app/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alonrbar%2Fredux-app/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alonrbar","download_url":"https://codeload.github.com/alonrbar/redux-app/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alonrbar%2Fredux-app/sbom","scorecard":{"id":186353,"data":{"date":"2025-08-11","repo":{"name":"github.com/alonrbar/redux-app","commit":"329db101e41af1ad04ee0da1fe24e22387a8a8e3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 0/30 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":"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":"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":"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":"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":"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":"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":"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.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md: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":"Vulnerabilities","score":0,"reason":"67 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-fwr7-v2mv-hh25","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-x9w5-v3q2-3rhw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-hr2v-3952-633q","Warn: Project is vulnerable to: GHSA-h6ch-v84p-w6p9","Warn: Project is vulnerable to: GHSA-vh7m-p724-62c2","Warn: Project is vulnerable to: GHSA-r9p9-mrjm-926w","Warn: Project is vulnerable to: GHSA-434g-2637-qmqr","Warn: Project is vulnerable to: GHSA-49q7-c7j4-3p7m","Warn: Project is vulnerable to: GHSA-977x-g7h5-7qgw","Warn: Project is vulnerable to: GHSA-f7q4-pwc6-w24p","Warn: Project is vulnerable to: GHSA-fc9h-whq2-v747","Warn: Project is vulnerable to: GHSA-vjh7-7g9h-fjfh","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","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-43f8-2h32-f4cj","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-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","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-4xcv-9jjx-gfj3","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-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","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-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-16T19:52:46.589Z","repository_id":57350214,"created_at":"2025-08-16T19:52:46.589Z","updated_at":"2025-08-16T19:52:46.589Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32018764,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T20:23:30.271Z","status":"online","status_checked_at":"2026-04-19T02:00:07.110Z","response_time":55,"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":["angular","dry","oop","react","redux","typescript"],"created_at":"2024-11-22T22:17:48.373Z","updated_at":"2026-04-19T19:02:25.092Z","avatar_url":"https://github.com/alonrbar.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-app\n\nType-safe, DRY and OO redux. Implemented with typescript.\n\n[![npm version](https://img.shields.io/npm/v/redux-app.svg)](https://www.npmjs.com/package/redux-app)\n[![npm license](https://img.shields.io/npm/l/redux-app.svg)](https://www.npmjs.com/package/redux-app)\n[![dependencies](https://david-dm.org/alonrbar/redux-app.svg)](https://github.com/alonrbar/redux-app)\n[![dependencies](https://david-dm.org/alonrbar/redux-app/dev-status.svg)](https://github.com/alonrbar/redux-app)\n\n[Change Log](https://github.com/alonrbar/redux-app/blob/master/CHANGELOG.md)\n\n## Installation\n\n```shell\nyarn add redux-app\n```\n\nor\n\n```shell\nnpm install --save redux-app\n```\n\n## Short Example\n\n```javascript\n\n//\n// declare \"reducers\" and state\n//\n\n@component\nclass App {\n    counter = new Counter();\n}\n\n@component\nclass Counter {\n    value = 0;\n\n    @action\n    increment() {\n        this.value = this.value + 1; // \u003c--- see Important Notice below\n    }\n}\n\n//\n// instantiate\n//\n\nconst app = new ReduxApp(new App());\n\n//\n// use\n//\n\nconsole.log(app.root.counter.value); // 0\nconsole.log(app.store.getState()); // { counter: { value: 0 } }\n\napp.root.counter.increment(); // will dispatch a 'Counter.increment' redux action\n\nconsole.log(app.root.counter.value); // 1\nconsole.log(app.store.getState()); // { counter: { value: 1 } }\n```\n\n## Important Notice\n\nYou should **not mutate** the object properties but rather assign them with new values.\nThat's why we write `this.value = this.value + 1` and not `this.value++`.\n\n## More Examples\n\nMore examples, including usage with [Angular](https://angular.io) and [React](https://reactjs.org/), can be found here [redux-app-examples](https://github.com/alonrbar/redux-app-examples).\n\n## How it works\n\nFor each decorated class the library generates an underlying `Component` object that holds the same properties and methods.\nThe new Component object has it's prototype patched and all of it's methods replaced with dispatch() calls.\nThe generated Component also has a hidden 'reducer' property which is later on used by redux store. The 'reducer' property itself is generated from the original object methods, replacing all 'this' values with the current state from the store on each call (using Object.assign and Function.prototype.call).\n\nTo make it easier to debug, each generated component name follows the following pattern: OriginalClassName_ReduxAppComponent (if while debugging you don't see the _ReduxAppComponent suffix it means the class was not replaced by an underlying component and is probably lacking a decorator, any of the following will do: @component, @action or @sequence).\n\n_Reading the source tip #1: There are two main classes in redux-app. The first is ReduxApp and the second is Component._\n\n## Documentation\n\n- [Stay Pure](#stay-pure)\n- Features\n  - [Async Actions](#async-actions)\n  - [Multiple Components of the Same Type](#multiple-components-of-the-same-type)\n  - [Computed Values (\"selectors\")](#computed-values)\n  - [Ignoring Parts of the State](#ignoring-parts-of-the-state)\n  - [Connect to a view](#connect-to-a-view)\n    - [React](#react)\n    - [Angular and others](#angular-and-others)\n- Utilities\n  - [isInstanceOf](#isinstanceof)\n- [Applying Enhancers (devtools, etc.)](#applying-enhancers)\n- Options\n  - [App Options](#app-options)\n  - [Global Options](#global-options)\n- [Changelog](https://github.com/alonrbar/redux-app/blob/master/CHANGELOG.md)\n\n### Stay Pure\n\nAlthough redux-app embraces a new syntax it still adheres to [the three principals of redux](http://redux.js.org/docs/introduction/ThreePrinciples.html):\n\n- The store is still the single source of truth. An automatic process propagates it to the components, similarly to what happens in react-redux.\n- The state is still read only. **Don't mutate the component's state directly**, only via actions (methods).\n- Changes are made with pure functions so keep your actions pure.\n\n### Async Actions\n\nAsync actions (thunks, sagas, epics...) and side effects are handled in redux-app by using the `sequence` decorator.\nWhat it does is to tell redux-app that the decorated method acts (almost) as a plain old javascript method. We say _almost_ since while the method body is executed regularly it still dispatches an action so it's still easy to track and log.\n\nRemember:\n\n- Don't change the state inside `sequence` methods.\n- If you need to dispatch a series of actions use the `sequence` decorator. Don't call actions from within other actions directly.\n\nUsage:\n\n_working example can be found on the [redux-app-examples](https://github.com/alonrbar/redux-app-examples) page_\n\n```javascript\n@component\nclass MyComponent {\n\n    @sequence\n    public async fetchImage() {\n\n        // dispatch an action\n        this.setStatus('Fetching...');\n\n        // do async stuff\n        var response = await fetch('fetch something from somewhere');\n        var responseBody = await response.json();\n\n        // dispatch another action\n        this.setStatus('Adding unnecessary delay...');\n\n        // more async...\n        setTimeout(() =\u003e {\n\n            // more dispatch\n            this.setStatus('I am done.');\n\n        }, 2000);\n    }\n\n    @action\n    public setStatus(newStatus: string) {\n        this.status = newStatus;\n    }\n}\n```\n\n### Multiple Components of the Same Type\n\nThe role of the `withId` decorator is double. From one hand, it enables the co-existence of two (or more) instances of the same component, each with it's own separate state. From the other hand, it is used to keep two separate components in sync. Every component, when dispatching an action attaches it's ID to the action payload. The reducer in it's turn reacts only to actions targeting it's component ID.\nThe 'id' argument of the decorator can be anything (string, number, object, etc.).\n\nExample:\n\n_working example can be found on the [redux-app-examples](https://github.com/alonrbar/redux-app-examples) page_\n\n```javascript\n@component\nexport class App {\n\n    @withId('SyncMe')\n    public counter1 = new Counter();  // \u003c-- this counter is in sync with counter2\n\n    @withId('SyncMe')\n    public counter2 = new Counter();  // \u003c-- this counter is in sync with counter1\n\n    @withId(123)\n    public counter3 = new Counter();  // \u003c-- manual set ID\n                                      //     this counter is not synced with the others\n    @withId()\n    public counter4 = new Counter();  // \u003c-- auto generated unique ID (unique within the scope of the application)\n                                      //     this counter also has it's own unique state\n}\n```\n\n### Connect to a view\n\nYou can leverage the following ReduxApp static method to connect your state components to your view:\n\n```javascript\nReduxApp.getComponent(componentType, componentId?, appId?)\n```\n\nYou can use IDs to retrieve a specific component or omit the ID to get the first instance that redux-app finds.\n\n#### React\n\n_working example can be found on the [redux-app-examples](https://github.com/alonrbar/redux-app-examples) page_\n\nUse the snippet below to create an `autoSync` function. You can then use it as you would normally use react-redux's `connect`:\n\n```jsx\nconst MyReactCounter: React.SFC\u003cCounter\u003e = (props) =\u003e (\n    \u003cdiv\u003e\n        \u003cspan\u003eValue: {props.value}\u003c/span\u003e\n        \u003cbutton onClick={props.increment}\u003eIncrement\u003c/button\u003e\n    \u003c/div\u003e\n);\n\nconst synced = autoSync(Counter)(MyReactCounter); // \u003c-- using 'autoSync' here\nexport { synced as MyReactComponent };\n```\n\nThe `autoSync` snippet:\n\n```javascript\nimport { connect } from 'react-redux';\nimport { Constructor, getMethods, ReduxApp } from 'redux-app';\n\nexport function autoSync\u003cT\u003e(stateType: Constructor\u003cT\u003e) {\n    return connect\u003cT\u003e(() =\u003e {\n        const comp = ReduxApp.getComponent(stateType);\n        const compMethods = getMethods(comp, true);\n        return Object.assign({}, comp, compMethods);\n    });\n}\n```\n\n#### Angular and others\n\n_working example can be found on the [redux-app-examples](https://github.com/alonrbar/redux-app-examples) page_\n\nWith Angular and similar frameworks (like Aurelia) it's as easy as:\n\n```javascript\nclass MyCounterView {\n    public myCounterReference = ReduxApp.getComponent(Counter);\n\n    // other view logic here...\n}\n```\n\n### Computed Values\n\nTo calculate values from other parts of the components state instead of using a fancy _selector_ function you can simply use a standard javascript getter.\n\n**Remember:** As everything else, getters should be pure and should not mutate the state.\n\nExample:\n\n_working example can be found on the [redux-app-examples](https://github.com/alonrbar/redux-app-examples) page_\n\n```javascript\n@component\nclass ComputedGreeter {\n\n    public name: string;\n\n    public get welcomeString(): string {\n        return 'Hello ' + this.name;\n    }\n\n    @action\n    public setName(newVal: string) {\n        this.name = newVal;\n    }\n}\n```\n\n### Ignoring Parts of the State\n\nYou can use the `ignoreState` decorator to prevent particular properties of your components to be stored in the store.\n\nExample:\n\n```javascript\n@component\nclass MyComponent {\n\n    public storeMe = 'I am stored';\n\n    @ignoreState\n    public ignoreMe = 'not stored';\n}\n\nconst app = new ReduxApp(new MyComponent());\n\nconsole.log(app.root); // { storeMe: 'I am stored', ignoreMe: 'not stored' }\nconsole.log(app.store.getState()); // { storeMe: 'I am stored' }\n```\n\n### isInstanceOf\n\nWe've already said that classes decorated with the `component` decorator are being replaced at runtime\nwith a generated subclass of the base Component class. This means you lose the ability to have assertions\nlike this:\n\n```javascript\n@component\nclass MyComponent {\n    // ...\n}\n\n// and elsewhere:\n\nif (!(obj instanceof MyComponent))\n    throw new Error(\"Invalid argument. Expected instance of MyComponent\");\n```\n\nLuckily redux-app supplies a utility method called `isInstanceOf` which you can use instead:\n\n```javascript\n@component\nclass MyComponent {\n    // ...\n}\n\n// and elsewhere:\n\nif (!isInstanceOf(obj, MyComponent))\n    throw new Error(\"Invalid argument. Expected instance of MyComponent\");\n```\n\nThe updated code will throw either if `obj` is instance of `MyComponent` or if it is an instance of a Component that was generated from the `MyComponent` class. In all other cases the call to `isInstanceOf` will return `false` and no exception will be thrown.\n\n### Applying Enhancers\n\nThe `ReduxApp` class has few constructor overloads that lets you pass additional store arguments (for instance, the awesome [devtool extension](https://github.com/zalmoxisus/redux-devtools-extension) enhancer).\n\n```javascript\nconstructor(appCreator: T, enhancer?: StoreEnhancer\u003cT\u003e);\n\nconstructor(appCreator: T, options: AppOptions, enhancer?: StoreEnhancer\u003cT\u003e);\n\nconstructor(appCreator: T, options: AppOptions, preloadedState: T, enhancer?: StoreEnhancer\u003cT\u003e);\n```\n\nExample:\n\n```javascript\nconst app = new ReduxApp(new App(), devToolsEnhancer(undefined));\n```\n\n### App Options\n\n```javascript\nexport class AppOptions {\n    /**\n     * Name of the newly created app.\n     */\n    name?: string;\n    /**\n     * By default each component is assigned (with some optimizations) with it's\n     * relevant sub state on each store change. Set this to false to disable\n     * this updating process. The store's state will still be updated as usual\n     * and can always be retrieved using store.getState().\n     * Default value: true.\n     */\n    updateState?: boolean;\n}\n```\n\nUsage:\n\n```javascript\nconst app = new ReduxApp(new App(), { updateState: false }, devToolsEnhancer(undefined));\n```\n\n### Global Options\n\n```javascript\nclass GlobalOptions {\n    /**\n     * Default value: LogLevel.Warn\n     */\n    logLevel: LogLevel;\n    /**\n     * Customize actions naming.\n     */\n    action: ActionOptions;\n}\n\nenum LogLevel {\n    /**\n     * Emit no logs\n     */\n    None = 0,\n    Verbose = 1,\n    Debug = 2,\n    Warn = 5,\n    /**\n     * Emit no logs (same as None)\n     */\n    Silent = 10\n}\n\nclass ActionOptions {\n    /**\n     * Add the class name of the object that holds the action to the action name.\n     * Format: \u003cclass name\u003e\u003cseparator\u003e\u003caction name\u003e\n     * Default value: true.\n     */\n    actionNamespace?: boolean;\n    /**\n     * Default value: . (dot)\n     */\n    actionNamespaceSeparator?: string;\n    /**\n     * Use redux style action names. For instance, if a component defines a\n     * method called 'incrementCounter' the matching action name will be\n     * 'INCREMENT_COUNTER'.\n     * Default value: false.\n     */\n    uppercaseActions?: boolean;\n}\n```\n\nUsage:\n\n```javascript\nReduxApp.options.logLevel = LogLevel.Debug;\nReduxApp.options.action.uppercaseActions = true;\n```\n\n### Changelog\n\nThe change log can be found [here](https://github.com/alonrbar/redux-app/blob/master/CHANGELOG.md).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falonrbar%2Fredux-app","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falonrbar%2Fredux-app","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falonrbar%2Fredux-app/lists"}