{"id":15099340,"url":"https://github.com/isocroft/radixx","last_synced_at":"2025-04-15T00:30:54.692Z","repository":{"id":15381695,"uuid":"78028715","full_name":"isocroft/Radixx","owner":"isocroft","description":"This is a simple library that implements the Facebook Flux Architecture with a twist to how the entire application state is managed and changed/updated. ","archived":false,"fork":false,"pushed_at":"2023-05-26T22:19:18.000Z","size":1301,"stargazers_count":19,"open_issues_count":10,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-11-27T23:28:33.317Z","etag":null,"topics":["actions-event-sourcing","actions-stack","async-flow","data-flow-management","facebook","facebook-flux-architecture","hydration-actions","javascript","mutation-actions","state-rebuild"],"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/isocroft.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":"coolcodes","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2017-01-04T15:40:52.000Z","updated_at":"2024-07-15T19:50:32.000Z","dependencies_parsed_at":"2024-09-15T17:22:28.061Z","dependency_job_id":null,"html_url":"https://github.com/isocroft/Radixx","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isocroft%2FRadixx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isocroft%2FRadixx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isocroft%2FRadixx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/isocroft%2FRadixx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/isocroft","download_url":"https://codeload.github.com/isocroft/Radixx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248984012,"owners_count":21193674,"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","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-event-sourcing","actions-stack","async-flow","data-flow-management","facebook","facebook-flux-architecture","hydration-actions","javascript","mutation-actions","state-rebuild"],"created_at":"2024-09-25T17:11:02.342Z","updated_at":"2025-04-15T00:30:54.674Z","avatar_url":"https://github.com/isocroft.png","language":"JavaScript","funding_links":["https://patreon.com/coolcodes"],"categories":[],"sub_categories":[],"readme":"# Radixx\r\n\r\n[![]()]() [![Build Status](https://travis-ci.org/isocroft/Radixx.svg?branch=master)](https://travis-ci.org/isocroft/Radixx) [![Node version](https://img.shields.io/node/v/radixx.svg?style=flat)](http://nodejs.org/download/) [![npm version](https://img.shields.io/npm/v/radixx.svg?style=flat)](https://www.npmjs.com/package/radixx)\r\n[![npm downloads](https://img.shields.io/npm/dm/radixx.svg?style=flat)](https://www.npmjs.com/package/radixx) [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/isocroft/Radixx/issues) [![license](http://img.shields.io/npm/l/geolocator.svg?style=flat)](https://github.com/isocroft/Radixx/blob/master/LICENSE.md) [![maintained](https://img.shields.io/maintenance/yes/2018.svg?style=flat)](https://github.com/isocroft/Radixx/graphs/commit-activity) [![Made in Nigeria](https://img.shields.io/badge/made%20in-nigeria-008751.svg?style=flat)](https://github.com/acekyd/made-in-nigeria) [![Join the chat at https://gitter.im/isocroft/Radixx](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/isocroft_Radixx/?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\r\n\r\nThis is a simple Javascript library that implements the **Facebook Flux Architecture** with a twist to how the entire application state is managed, changed and updated. It resembles **Redux** in a lot of ways. The key deferentiator in both is that **Radixx** utilizes an _actions stack_ and **Redux** utilizes an immutable _state tree_. A single state tree can grow big really fast for a single store but an actions stack grows subtlely for a number of stores when dealing with complex single-page applications(SPAs). The actions stack allows **Radixx** to *\u003cq\u003erecalulate\u003c/q\u003e* any state at anytime on demand. It is also how **Radixx** maintains it's _immutablility_.\r\n\r\n![radixx flow](Radixx_Flow.PNG)\r\n\r\n## File Size \r\n\r\nThe footprint for **Radixx** is really small. With all it's functionality, it's just 19KB when minified!\r\n\r\n- 48.5 KB (Current Version - Unminified)\r\n- 19.3 KB (Current Version - Minified)\r\n\r\n\u003cimg src=\"radixx.png\"\u003e\u003c/img\u003e\r\n\r\n## Quick Start in 2 steps\r\n\r\n- First, download/install it from **npm** or **yarn**\r\n\r\n[Radixx NPM Page][npm-url]\r\n\r\n\r\n```bash\r\n\r\n\t$ npm i radixx --save\r\n```\r\n\r\n```bash\r\n\r\n\t$ yarn add radixx\r\n```\r\n\r\n- Next, use it in your JavaScript application like so: \r\n\r\n```html\r\n\r\n\u003c!DOCTYPE html\u003e\r\n\u003chtml id=\"demo_app\"\u003e\r\n\u003chead\u003e\r\n\t\u003ctitle\u003eRadixx (Vanilla) - Example App\u003c/title\u003e\r\n\t\u003c!-- relative path (NodeJS)\r\n\t\u003cscript type=\"text/javascript\" src=\"node_modules/radixx/dist/radixx.min.js\"\u003e\u003c/script\u003e --\u003e\r\n\t\u003c!-- absolute path (Unpkg) via CDN --\u003e\r\n\t\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/radixx@0.1.3/dist/radixx.min.js\"\u003e\u003c/script\u003e\r\n\t\u003cscript type=\"text/javascript\"\u003e\r\n\t;(function(w, r){\r\n\r\n\t\tr.onDispatch(function(app_state){\r\n\t\t  /* fired when all synchronous/asynchronous \r\n\t\t  mutations are completely done on the state */\r\n\t\t  console.log(JSON.stringify(app_state)); \r\n\t\t});\r\n\r\n\t\tr.configure({\r\n\t\t\truntime:{ /* The feature/functionality that the {runtime} config controls is still EXPERIMENTAL */\r\n\r\n\t\t\t\tspaMode:true, \r\n\t\t\t\t\t/* - {runtime.spaMode} \r\n\r\n\t\t\t\t\t\tsetup for whether or not you're building a SPA web app (e.g. using VueJS / ReactJS ) \r\n\r\n\t\t\t\t\t\tif {runtime.spaMode} is set to `false`, Radixx will automatically setup (internally) its\r\n\t\t\t\t\t\tASMLC [ Application State Management Life Cycle ] procedures.\r\n\r\n\t\t\t\t\t\tThis ensures that you are able to be notified when the page is about to be unloaded\r\n\t\t\t\t\t*/\r\n\t\t\t\tshutDownHref:'/'\r\n\t\t\t\t\t/* - {runtime.shutDownHref}\r\n\r\n\t\t\t\t\t\tmakes it trivial to trigger the the shutdown callback/handler set by `Radixx.onShutdown`.\r\n\t\t\t\t\t\tThis config should be set to the value of the `href` or `data-href` attribute of a link or\r\n\t\t\t\t\t\tbutton that triggers/signals the end of the web application session (e.g a logout button) \r\n\r\n\t\t\t\t\t*/\r\n\t\t\t},\r\n\t\t\tuniversalCoverage:true, \r\n\t\t\t\t/* - {universalCoverage}\r\n\r\n\t\t\t\t\tmake store state changes in one tab available across browser tabs \r\n\t\t\t\t\tand update the state on the other tabs (pages) accordingly \r\n\t\t\t\t*/\r\n\t\t\tautoRehydrate:true, \t\r\n\t\t\t\t/* - {autoRehydrate}\r\n\r\n\t\t\t\t\tautomatically hydrate all stores with their respective state data \r\n\t\t\t\t\tupon config initialization so that your state is available to all\r\n\t\t\t\t\tstores as soon as your components load without having to retrieve\r\n\t\t\t\t\tdata over the network. \r\n\r\n\t\t\t\t\tGreat for PWAs!! :)\r\n\t\t\t\t*/\r\n\t\t\tpersistenceEnabled:false, \r\n\t\t\t\t/* - {persistenceEnabled}\r\n\r\n\t\t\t\t\tmake your state change persist even after the browser is closed\r\n\r\n\t\t\t\t\tYou don't need this config to persist state within the same browser\r\n\t\t\t\t\tsession (i.e. across page loads/refreshes).\r\n\r\n\t\t\t\t\tIMPORTANT: \r\n\t\t\t\t\t\tOnce {universalCoverage} is set to `true`, \r\n\t\t\t\t\t\tthe value of {persistenceEnabled} is automatically set to `true` \r\n\t\t\t\t */\r\n\t\t\tlocalHostDev:true \t\t\r\n\t\t\t\t/* - {localHostDev}\r\n\r\n\t\t\t\t\tWorks as a helper config that's used with {universalCoverage} for when [Radixx] is\r\n\t\t\t\t\tused in a 'localhost' developement environment. If Radixx is used in a production app,\r\n\t\t\t\t\tset this config to `false`.\r\n\r\n\t\t\t\t\tWhenever this config is set to `true`, it's important an 'id' attribute on the \u003chtml\u003e\r\n\t\t\t\t\ttag with a unique value.  \r\n\t\t\t\t*/\r\n\t\t});\r\n\r\n\t\tvar registeredStores = [];\r\n\t\t\r\n\t\t/* \r\n\t\t  creating action creators - [ multiple actions can be created for a real-life application ]\r\n\t\t*/\r\n\t\tw.action_creators = r.makeActionCreators({\r\n\t\t\tloadTodos:{\r\n\t\t\t\ttype:'LOAD_TODOS',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.array /* Filter \u0026 Validate action data as `array` for every dispatch */\r\n\t\t\t},\r\n\t\t\tsaveTodo:{\r\n\t\t\t\ttype:'SAVE_TODO',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.object /* Filter \u0026 Validate action data as `object` for every dispatch */\r\n\t\t\t}\t\r\n\t \t});\r\n\r\n\t \t/*\r\n\r\n\t\t\tMiddlewares allow you to modify the existing action data in sequence until it\r\n\t\t\tgets dispatched to all available stores.\r\n\r\n\t\t\tPS: You can only access the `nextState` (latest application state) in the last\r\n\t\t\t\tattached middleware (according to the order in which the middlewares were attached)\r\n\r\n\t\t\t\tIf you MUST access the `nextState` in the remaining middleware(s) other than \r\n\t\t\t\tthe last one, then return the `nextState` from the last attached middleware\r\n\t \t*/\r\n\r\n\t\tr.attachMiddleware(function(next, action, prevState){\r\n\r\n\t\t\t\t\tlet promise = new Promise((resolve, reject) =\u003e {\r\n\r\n\t\t\t\t\t\tif(action.actionType === 'PUSH_DOWN'){\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tif(action.actionData.expired === true){\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\tsetTimeout(() =\u003e {\t\r\n\r\n\t\t\t\t\t\t\t\t \t\tvar data = {\r\n\t\t\t\t\t\t\t\t \t\t\tname:'whatever'\r\n\t\t\t\t\t\t\t\t \t\t};\r\n\r\n\t\t\t\t\t\t\t\t \t\tresolve(\r\n\t\t\t\t\t\t\t\t \t\t\tdata\r\n\t\t\t\t\t\t\t\t \t\t); \r\n\r\n\t\t\t\t\t\t\t\t}, 4500);\r\n\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t}else{\r\n\r\n\t\t\t\t\t\t\t\tresolve(\r\n\t\t\t\t\t\t\t\t\taction.actionData\r\n\t\t\t\t\t\t\t\t)\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t});\r\n\t\t\t\r\n\t\t\t\t\tpromise.then(function(data){\r\n\r\n\t\t\t\t\t\t/* \r\n\t\t\t\t\t\t\tsignal to Radixx that it should call the next middleware \r\n\t\t\t\t\t\t\tcallback or dispatch to all stores.\r\n\t\t\t\t\t\t*/\r\n\r\n\t\t\t\t \t\taction.actionData = data;\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tvar result = next(\r\n\t\t\t \t\t\t\taction,\r\n\t\t\t \t\t\t\tprevState\r\n\t\t\t \t\t\t);\r\n\r\n\t\t\t \t\t\tconsole.log(result.nextState); // To access the `nextState` from the next middleware callback\r\n\t\t \t\t\t});\r\n\r\n\t\t});\r\n\r\n\t\t/*\r\n\r\n\t\t\tA Logger Middleware - logs to the browser console!\r\n\t\t*/\r\n\r\n\t\tr.attachMiddleware(function(next, action, prevState){\r\n\r\n\t\t\tconsole.log('action-data', action.actionData);\r\n\r\n\t\t\tconsole.log('action-type', action.actionType);\r\n\r\n\t\t\tconsole.log('before-action', prevState);\r\n\r\n\t\t\tvar nextState = next(\r\n\t\t\t\taction,\r\n\t\t\t\tprevState\r\n\t\t\t);\r\n\r\n\t\t\tconsole.log('after-action', nextState);\r\n\r\n\t\t\treturn {\r\n\t\t\t\tnextState:nextState, // returned to the previous middleware callback\r\n\t\t\t\tlogType:'local'\r\n\t\t\t};\r\n\r\n\t\t});\r\n\r\n\t\t\r\n\t\t/* creating a store - [ however, multiple stores can be created for a real-life application ] */\r\n\r\n\t\t/* \r\n\t\t\tthere's a strict structure to how to define a store callback - MUST recieve 2 arguments (the action object and state object) and always return the new state object \r\n\r\n\t\t \tstore callbacks generally SHOULD be pure functions (having no side effects)\r\n\t\t*/\r\n\r\n\t\t/*\r\n\t\t\t As from version 0.1.0+ of Radixx, the second argument is no longer an object with \r\n\t\t\t getters/setter (formerly named - area).\r\n\r\n\t\t\t Now, the second argument is the state object itself\r\n\t\t */\r\n\t\tw.store = r.makeStore('todos', function(action, state){\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tvar todos = state; \r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tswitch(action.actionType){\r\n\t\t\t\t\t\t\tcase 'LOAD_TODOS':\r\n\t\t\t\t\t\t\t\t// action.actionData MUST be an array as seen\r\n\t\t\t\t\t\t\t\t// from the action.loadTodos([ ... ]) call below\r\n\t\t\t\t\t\t\t\ttodos = state.concat.apply(action.actionData, [\"empty\"]);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tcase 'SAVE_TODO':\r\n\t\t\t\t\t\t\t\ttodos.push(action.actionData);\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t\tdefault:\r\n\t\t\t\t\t\t\t\treturn null; // if control reaches here, then state didn't change at all !\r\n\t\t\t\t\t\t\tbreak;\r\n\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\treturn todos;\r\n\t\t}, []);\r\n\r\n\t\t// setup a function to listen for change to the store\r\n\t\tstore.setChangeListener(function(actionType, actionKey){\r\n\r\n\t\t\t// the this reference in here is the store itself\r\n\t\t\talert(\"STORE AFFECTED: \" + this.getTitle());\r\n\t\t});\r\n\r\n\t\t// do something with each store created\r\n\t\tr.eachStore(function(store, next){\r\n\r\n\t\t\t// get the title of the store and push into an array\r\n\t\t\tregisteredStores.push(store.getTitle());\r\n\r\n\t\t\t// move to the next store that has been created and do same as above with it (very useful for asynchronous code)\r\n\t\t\tnext();\r\n\t\t});\r\n\r\n\t\t/*\r\n\t\t\t hydrate all stores simultaneously at page load for the entire app \r\n\t\t\t (example custom logic for web app) after ajax request.\r\n\t\t*/\r\n\r\n\t\tvar Application = {\r\n\t\t\t\tloadAllState:function(httpresponse){\r\n\t\t\t\t\t\r\n\t\t\t\t\t// hypotectical REST/GraphQL endpoint data\r\n\r\n\t\t\t\t\treturn r.eachStore(function(store, next){\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t// hydrate each store in one after the other\r\n\t\t\t\t\t\t// using the `next()` function \r\n\r\n\t\t\t\t\t\t// This can also be used in an asynchronous fashion\r\n\t\t\t\t\t\tnext(store.hydrate(httpresponse.data[store.getTitle()]));\r\n\r\n\t\t\t\t\t});\r\n\t\t\t\t},\r\n\t\t\t\tajaxRequest:function(options){\r\n\r\n\t\t\t\t\tconst promise = new Promise((resolve, reject) =\u003e {\r\n\r\n\t\t\t\t\t\t\t\t// We are trying to find out if all stores\r\n\t\t\t\t\t\t\t\t// were auto-rehydrated from the persistent storage\r\n\t\t\t\t\t\t\t\t// if so, we don't need to make any AJAX request to\r\n\t\t\t\t\t\t\t\t// retrieve data over the network. Why?\r\n\r\n\t\t\t\t\t\t\t\t// because our stores already have the state data needed\r\n\t\t\t\t\t\t\t\t// to power our application.\r\n\t\t\t\t\t\t\t\tif(r.isAppStateAutoRehydrated()){\r\n\t\t\t\t\t\t\t\t\treturn resolve(\"okay!\");\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\t// Go over the network to retrieve data \r\n\t\t\t\t\t\t\t\t// if our all stores were not auto-rehydrated\r\n\r\n\t\t\t\t\t\t\t\tvar xhr = new XMLHttpRequest();\r\n\r\n\t\t\t\t\t\t\t\txhr.setRequestHeader(\"Accept\", \"application/json, text/plain\");\r\n\r\n\t\t\t\t\t\t\t\txhr.onload = function(e){\r\n\t\t\t\t\t\t\t\t\tresolve(xhr.responseText);\r\n\t\t\t\t\t\t\t\t};\r\n\r\n\t\t\t\t\t\t\t\txhr.onerror = function(e){\r\n\t\t\t\t\t\t\t\t\treject(xhr.statusText);\r\n\t\t\t\t\t\t\t\t}\r\n\r\n\t\t\t\t\t\t\t\txhr.open(options.url, options.type, true);\r\n\r\n\t\t\t\t\t\t\t\txhr.send(options.data);\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\treturn promise;\r\n\t\t\t\t}\r\n\t\t};\r\n\r\n\t\tApplication.ajaxRequest({\r\n\t\t\turl:\"read/all/models\",\r\n\t\t\ttype:\"GET\",\r\n\t\t\tdata:null\r\n\t\t}).then(function(res){\r\n\t\t\tif(res !== \"okay!\"){\r\n\t\t\t\treturn Application.loadAllState(\r\n\t\t\t\t\t\t\tJSON.parse(res)\r\n\t\t\t\t);\r\n\t\t\t}\r\n\t\t\treturn res;\r\n\t\t});\r\n\r\n\t\t// Setup `shutdown` handler which is triggered whenever the shutdown-href\r\n\t\t// is clicked or the page is unloaded\r\n\t\tr.onShutdown(function(appState){\r\n\r\n\t\t\tvar self = this; // The `Radixx` object is captured in `this`\r\n\r\n\t\t\t// Here, we are sending all the data taken\r\n\t\t\t// from all the stores {appState} to the\r\n\t\t\t// server just before the page unloads completely\r\n\t\t\treturn Application.ajaxRequest({\r\n\t\t\t\turl:\"write/all/models\",\r\n\t\t\t\ttype:\"PUT\",\r\n\t\t\t\tdata:appState\r\n\t\t\t}).then(function(){\r\n\t\t\t\t// remove the auto-rehydration state from the\r\n\t\t\t\t// perstsent storage\r\n\t\t\t\tself.purgePersistentStorage();\r\n\t\t\t});\r\n\t\t});\r\n\r\n\t\t// swapping the store callback with a new one\r\n\r\n\t\tstore.swapCallback(function(action, state){\r\n\t\t\t\t\r\n\t\t\t\tvar todos = state; \r\n\t\t\t\t\r\n\t\t\t\tswitch(action.actionType){\r\n\t\t\t\t\tcase 'LOAD_TODOS':\r\n\t\t\t\t\t\t/* \r\n\t\t\t\t\t\t\tsetting up the format the state data should be stored before\r\n\t\t\t\t\t\t\tit is put in storage. since all store data is stored in JSON\r\n\t\t\t\t\t\t*/ \r\n\t\t\t\t\t\ttodos.toJSON = function(){\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tvar todoTimeToDueDate = (new Date*1),\r\n\t\t\t\t\t\t\t\talltodos = this.concat.apply(action.actionData);\r\n\r\n\t\t\t\t\t\t\t\treturn alltodos.map(function(todo){\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\t\t\treturn {\r\n\t\t\t\t\t\t\t\t\t\tis_overdue:todoTimeToDueDate \u003e todo.due_date_timestamp,\r\n\t\t\t\t\t\t\t\t\t\ttodo:todo\r\n\t\t\t\t\t\t\t\t\t};\r\n\r\n\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t};\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t\tdefault:\r\n\t\t\t\t\t\treturn null;\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn todos;\r\n\t\t});\r\n\r\n\t\tconsole.log(\"stores registered: \", registeredStores.toString(), registeredStores.length);\r\n\r\n\t}(this, this.Radixx));\t\r\n\t\u003c/script\u003e\r\n\u003c/head\u003e\r\n\u003cbody\u003e\r\n\t\u003cul id=\"todos\"\u003e\r\n\t\u003c/ul\u003e\r\n\t\u003cbutton type=\"button\" disabled=\"disabled\" id=\"undo-btn\"\u003e\r\n\t\tUNDO\r\n\t\u003c/button\u003e\r\n\t\u003cscript type=\"text/javascript\"\u003e\r\n\r\n\t\t;(function(w, d){\r\n\r\n\t\t\t// calling an action - an action triggers changes in the store (by extension the application state)\r\n\t\t\tw.action_creators.loadTodos([\r\n\t\t\t\t{\r\n\t\t\t\t\ttext:'Buy flowers for my fiancee', \r\n\t\t\t\t\tcompleted:true,\r\n\t\t\t\t\tdue_date_timestamp:1491144056023\r\n\t\t\t\t},\r\n\t\t\t\t{\r\n\t\t\t\t\ttext:'Prepare that website re-design proposal',\r\n\t\t\t\t\tcompleted:false,\r\n\t\t\t\t\tdue_date_timestamp:1491144702573\r\n\t\t\t\t}\r\n\t\t\t]);\r\n\r\n\r\n\t\t\tvar mount_point = d.getElementById(\"todos\");\r\n\t\t\tvar button = d.getElementById(\"undo-btn\");\r\n\t\t\t\r\n\t\t\tfunction render(m){\r\n\t\t\t\t\tvar list = w.store.getState();\r\n\t\t\t\t\tvar li = null;\r\n\r\n\t\t\t\t\tif(list.length == 0){\r\n\t\t\t\t\t\tm.innerHTML = \"\";\r\n\t\t\t\t\t\treturn;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\tlist.forEach(function(item, i){\r\n\t\t\t\t\t\tli = d.createElement(\"li\");\r\n\t\t\t\t\t\tli.setAttribute(\"data-key\", i);\r\n\t\t\t\t\t\tli.setAttribute(\"data-todo-overdue\", item.is_overdue);\r\n\t\t\t\t\t\tli.setAttribute(\"data-todo-done\", String(item.todo.completed));\r\n\t\t\t\t\t\tli.appendChild(d.createTextNode(item.todo.text));\r\n\t\t\t\t\t\tm.appendChild(li);\r\n\t\t\t\t\t});\r\n\t\t\t}\t\t\r\n\r\n\t\t\tbutton.disabled = !w.store.canUndo();\r\n\t\t\tbutton.onclick = function(e){\r\n\t\t\t\t// undo application state changes\r\n\t\t\t\tw.store.undo();\r\n\t\t\t\tthis.disabled = !w.store.canUndo();\r\n\t\t\t\trender(mount_point);\r\n\t\t\t};\r\n\r\n\t\t\trender(mount_point);\r\n\r\n\t\t}(this, this.document));\r\n\t\u003c/script\u003e\r\n\u003c/body\u003e\r\n\u003c/html\u003e\r\n```\r\n\r\n\r\n## DOCUMENTATION - Radixx APIs (Methods)\r\n\r\nThese are methods defined on the global **Radixx** object\r\n\r\n- **Radixx.attachMiddleware(** _Function_ middlewareCallback **)** : _void_\r\n\r\n\u003e Used to intercept actions that are dispatched by action creators to stores and/or modify action data where necesssary before they reach the store.\r\n\r\n- **Radixx.isAppStateAutoRehydrated(** _void_ **)** : _boolean_\r\n\r\n\u003e Used is interogate the auto-rehydration process to find out if the application state data was restored from the persistent storage on the client-side or not.\r\n\r\n- **Radixx.makeActionCreators(** _Object_ actionTagMap **)** : _object_ {RadixxActionCreator}\r\n\r\n\u003e Used to create an action creators (in Flux Architecture parlance). An object literal having the action method names (as key) and the action tag (as value) is passed into this api method.\r\n\r\n- **Radixx.makeStore(** _String_ storeTitle, _Function_ storeCallback [, _Array|Object_ initialStoreState] **)** : _object_ {RadixxStore}\r\n\r\n\u003e Used to create a store to which action will be sent using action creators.\r\n\r\n- **Radixx.purgePersistentStorage(** _void_ **)** : _void_\r\n\r\n\u003e Used to delete all the persisted data for auto-rehydration. \r\n\r\n- **Radixx.onShutdown(** _Function_ shutdownListener **)** : _void_\r\n\r\n\u003e Registers a listener that is called whenever Radixx automatically destroys all its stores internally due to a `shutdown-href` trigger.\r\n\r\n- **Radixx.onDispatch(** _Function_ dispatchListener **)** : _void_\r\n\r\n\u003e Registers a listener that is called whenever an action is disptached to the Hub (also called the `Dispatcher` in Flux Architecture parlance).\r\n\r\n- **Radixx.eachStore(** _Function_ storeIterator **)** : _void_\r\n\r\n\u003e Used to call a function on each store created by Radixx and perform some operation on that store (operation is defined with the storeIterator).\r\n\r\n- **Radixx.configure(** _Object_ configurationObject **)** : _void_\r\n\r\n\u003e Used to configure the Radixx store(s). \r\n\r\n### ----------------------------------------------------------------------------------------------------------------\r\n\r\n## Radixx Configuration Options\r\n\r\n- **runtime.spaMode** {boolean} [ EXPERIMENTAL FEATURE ]\r\n\r\n\u003e Whether or not the app in an SPA (Single Page Application). If the app is a SPA, then the _shutDownHref_ option doesn't have any effect.\r\n\r\n- **runtime.shutDownHref** {string} [ EXPERIMENTAL FEATURE ]\r\n\r\n\u003e The Href/Link upon which Radixx should destroy all stores and automatically shutdown state tracking. Also triggers the \"Radixx.onShutDown()\" handler function set prior to call.\r\n\r\n- **universalCoverage** {boolean}\r\n\r\n\u003e Makes it possible to replicates state changes across multiple browser tabs/windows\r\n\r\n- **persistenceEnabled** {boolean}\r\n\r\n\u003e Makes it possible to persist application state across browser session\r\n\r\n- **autoRehydrate** {boolean}\r\n\r\n\u003e Makes it possible to restore persisted state data into all pre-existing stores\r\n\r\n### ----------------------------------------------------------------------------------------------------------------\r\n\r\n## Radixx APIs (Properties)\r\n\r\n- **Radixx.Helpers**\r\n\r\n\u003e Used to provide ancillary logic to SPA framework/library compoenents (e.g. ReactJS)\r\n\r\n- **Radixx.Payload.type.boolean**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Boolean\r\n\r\n- **Radixx.Payload.type.string**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: String\r\n\r\n- **Radixx.Payload.type.array**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Array\r\n\r\n- **Radixx.Payload.type.object**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Object\r\n\r\n- **Radixx.Payload.type.date**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Date\r\n\r\n- **Radixx.Payload.type.function**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Function\r\n\r\n- **Radixx.Payload.type.number**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Number\r\n\r\n- **Radixx.Payload.type.nullable**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be of type: Null or Undefined\r\n\r\n- **Radixx.Payload.type.any**\r\n\r\n\u003e Used to signify that the action creator method argument/payload CAN BE of any type EXCEPT: Null or Undefined\r\n\r\n- **Radixx.Payload.type.numeric.Int**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be a Integer number\r\n\r\n- **Radixx.Payload.type.numeric.Float**\r\n\r\n\u003e Used to signify that action creator method argument/payload MUST always be a Floating-Point number\r\n\r\n### ----------------------------------------------------------------------------------------------------------------\r\n\r\n## Store APIs\r\n\r\nThese are methods defined on the store object from **Radixx.makeStore( ... )** call\r\n\r\n- **store.hydrate(** _Array|Object_ initialStoreData **)**\r\n\r\n\u003e Used to put/insert/overwrite state data in the store directly without using an action creator. This call affects only the store in context. However, whenever actions are dispatched by an action creator, they affect all stores in the web application.\r\n\r\n- **store.getTitle(** _void_ **)**\r\n\r\n\u003e Used to retrieve the storeTitle used in creating the store.\r\n\r\n- **store.getState(** _void|String_ stateKey **)**\r\n\r\n\u003e Used to retrieve the current state of the store.\r\n\r\n- **store.makeTrait(** _Function_  traitFactory **)**\r\n\r\n\u003e Used to create data items which are based on the store.\r\n\r\n- **store.setChangeListener(** _Function_ changeListener **)**\r\n\r\n\u003e Used to register a listener (callback) that is called whenever the state of the store changes\r\n\r\n- **store.unsetChangeListener(** _Function_ changeListener **)**\r\n\r\n\u003e Used to unregister a listener (callback)\r\n\r\n- **store.undo(** _void|Number_ timeSpan **)**\r\n\r\n\u003e Used to undo a state change to the store\r\n\r\n- **store.redo(** _void|Number_ timeSpan **)**\r\n\r\n\u003e Used to redo a state change to the store\r\n\r\n- **store.swapCallback(** _Function_ newStoreCallback **)**\r\n\r\n\u003e Used to hot swap / replace a store callback\r\n\r\n- **store.destroy(** _void_ **)**\r\n\r\n\u003e Used to remove store state data from sessionStorage (manually)\r\n\r\n- **store.disconnect(** _void_ **)**\r\n\r\n\u003e Used to disbale the store from recieving dispatch from action creators methods\r\n\r\n\r\n## Features\r\n\r\n- Infinite/Finite Undo/Redo + Time Travel \r\n- Use of Traits (as Mixins) for ReactJS and VueJS (even though most people think _mixins_ are dead and composition should be the only thing in used, i still think mixins have a place)\r\n- Can replace the store callback (similar to **replaceReducer()** in _Redux_)\r\n- Can overwrite store state data on the fly using **hydrate()**\r\n- An extended Loose Coupling between Radixx Dispatcher and Controllers/Components.\r\n- A transparent way for separating mutation, dependence and asynchronousity in application state management.\r\n- No need to **\u003cq\u003eemit\u003c/q\u003e** change events from within a store registration callback.\r\n- No unecessarily elaborate definition for **Action Creators** \u0026amp; **Stores** (Write less code).\r\n- No need to use immutableJS library to help manage application state changes (Require state on demand).\r\n- The **Dispatcher** object is hidden and all you have is a **\u003cq\u003eHub\u003c/q\u003e** object.\r\n- Includes an **\u003cq\u003eonDispatch\u003c/q\u003e** event that exposes the entire global application state (from all stores).\r\n\r\n## Benefits\r\n\r\n- Use with Single Page App Frameworks/Libraries e.g. VueJS 1.x/2.x, AngularJS 1.x, Angular 2.x, Ractive, ReactJS, jQuery\r\n- Use with Multi Page App Frameworks as well e.g Django, Jollof, Pheonix, Laravel, Flask, AdonisJS (application state persists across page(s) load/reload)\r\n\r\n## Best Practices (Dos and Don'ts)\r\n\r\n- Application UI State {a.k.a Volatile Data} -- **don't store** this in Radixx stores (e.g. Text Input - being entered, Animation Tween Properties/Values, Scroll Position Values, Text Box Caret Position, Mouse Position Values, Unserializable State - like functions)\r\n- Application Data State {a.k.a Non-Volatile Data} -- **do store** this in Radixx stores (e.g. Lists for Render fetched from API endpoints, any piece of Data displayed on the View)\r\n\r\n\u003e NOTE: When using Radixx with ReactJS, it is best to ascribe/delegate Application UI State to {this.state} and {this.setState(...)} and Application Domain Data State to {this.props} respectively. One reason why Radixx recommends this approach is to avoid confusion as to when {this.setState} calls actually update both the DOM and {this.state} since {this.setState} is sometimes _asynchronous_ in the way it operates.\r\n\r\n## About Redux (with respect to Radixx)\r\n\r\nHers's how **Redux** is simply described\r\n\r\n\u003e Redux is basically event-sourcing where there is a single projection to consume the application state.\r\n\r\nSomeone had this to say about the state of **Redux** single store\r\n\r\n\u003e I'm also not impressed about every action having to go “all the way” upwards (to the central store) instead of short-circuiting somewhere.\r\n\r\nThat being said, here is a round-up of what makes **Redux** a GREAT tool (for some use cases) and what doesn't make it so ideal (for all use cases)\r\n\r\n# Gains of Redux single store\r\n\r\n- Infinte Undo/Redo + Live-Editing Time Travel (As application state is immutable).\r\n- Predictable Atomic Operation on Application state object (As actions are run in a specific predictable order).\r\n- Single source of truth (No Guesswork!!) for applicaton state.\r\n\r\nSame applies to Radixx\r\n\r\n# Troubles with Redux single store\r\n\r\n- A sizable amount of boilerplate code (especially with **connect()** and/or **mapStateToProps()** from _react-redux_ project library) is required to get Redux up and running.\r\n- Dynamically structured state is impossible. (mature, complex apps need this the most).\r\n- Increased probability of state key(s) collisions between reducers (very likely in a big complex web app but highly unlikely in samll ones).\r\n- Global variables are most times a bad thing (This applies to the composition of the Redux application state itself) as you could `clobber` them unknowingly.\r\n- Performance suffers as your state tree gets larger (Immutability is a good thing...sometimes).\r\n- Each time the **connect()** decorator is called, it pulls in the entire application state (when using _react-redux_ project library).\r\n\r\n\r\n_Your best bet in all these is to choose the trade-offs wisely (depending on the peculiarities of the web app you're building)_. In order words, Choose what you can live with and what you can't live without when it comes to having a store in your application.\r\n\r\n\r\n\u003e NOTE: **Radixx** is not an outright drop-in replacement for **Redux** or **Vuex** but a nice alternative when **Redux** or **Vuex** doesn't quite fit your use case.\r\n\r\n\r\n## Tests\r\n\r\nThis project uses **Jasmine** for tests and runs them on the command line with **Karma** .You can run to check the quality of code written by running the tests in 2 ways\r\n\r\n1. run the following command in the root folder after cloning the repo\r\n\r\n```bash\r\n\r\n\t$ npm install\r\n\r\n\t$ npm run test\r\n```\r\n\r\n2. open the _tests/specRunner.html_ file in any browser of your choice using the file:/// protocol\r\n\r\n\r\n## Examples\r\n\r\n\u003eVueJS 1.x/2.x\r\n\r\n```js\r\n\r\n\tvar action_creators = Radixx.makeActionCreators({\r\n\t\t\t'addStuff':{\r\n\t\t\t\ttype:'ADD_STUFF',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.string\r\n\t\t\t}\r\n\t});\r\n\r\n\tvar store = Radixx.makeStore('stuffs', function(action, state){\r\n\t\t\t\r\n\t\t\tvar stuffs = state;\r\n\r\n\t\t\tswitch(action.actionType){\r\n\t\t\t\tcase 'ADD_STUFF':\r\n\r\n\t\t\t\t\t/* action.actionKey is 'profile' */\r\n\r\n\t\t\t\t\tif(action.actionKey\r\n\t\t\t\t\t\t\u0026\u0026 !!stuffs[action.actionKey]){\r\n\t\t\t\t\t\tstuffs[action.actionKey].fullName = action.actionData;\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\t\treturn null;\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\r\n\t\t\treturn stuffs;\r\n\t}, {\r\n\t\tprofile:{\r\n\t\t\tfullName:'John',\r\n\t\t\tphone:'08011111111'\r\n\t\t}\r\n\t});\r\n\r\n\tvar trait = store.makeTrait(function(store, callback){\r\n\r\n\t\t\tvar __callback;\r\n\r\n\t\t\treturn {\r\n\t\t\t\t_loadAppData:function(){\r\n\r\n\t\t\t\t\t// an ajax call may come in here ...\r\n\r\n\t\t\t\t\t/*\t\r\n\t\t\t\t\t\tfrom an ajax call response, if an empty object literal was passed (as third argument) to `Radixx.createStore` call (above), then, we can call `hydrate` on the store object here passing it the requisite data from the ajax response\r\n\r\n\t\t\t\t\t\tAssuming `axios` is loaded\r\n\r\n\t\t\t\t\t*/\r\n\r\n\t\t\t\t\tif(!Radixx.isAppStateAutoRehydrated()){\r\n\t\t\t\t\t\taxios.get('/get/data')\r\n\t\t\t\t\t\t\t.then(function( ajaxResponseData){\r\n\t\t\t\t\t\t\t\tstore.hydrate( ajaxResponseData );\r\n\t\t\t\t\t\t})\r\n\t\t\t\t\t\t\t.catch(function(error){\r\n\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\t}\r\n\t\t\t\t},\r\n\t\t\t\tbeforeCreate:function(){\r\n\t\t\t\t\r\n\t\t\t\t\tRadixx.configure({\r\n\t\t\t\t\t\tautoRehydrate:true\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tthis._loadAppData();\r\n\r\n\t\t\t\t},\r\n\t\t\t\tcreated:function(){\r\n\r\n\t\t\t\t\tconst _this = this;\r\n\r\n\t\t\t\t\t// register a dispatch listener...\r\n\r\n\t\t\t\t\tRadixx.onDispatch(function(appState){\r\n\r\n\t\t\t\t\t\t// do stuff with `appState`\r\n\r\n\t\t\t\t\t\t var componentName = _this.$options.name;\r\n\r\n\t\t\t\t\t\t console.log(appState[componentName]);\r\n\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t},\r\n\t\t\t\tbeforeMount:function(){\r\n\r\n\t\t\t\t\tconst _this = this;\r\n\r\n\t\t\t\t\t// register a middleware callback...\r\n\r\n\t\t\t\t\tRadixx.attachMiddleware(function(next, action, prevState){\r\n\r\n\t\t\t\t\t\tvar nextState = next(\r\n\t\t\t\t\t\t\taction,\r\n\t\t\t\t\t\t\tprevState\r\n\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\t// you could chose to emit an event here too..\r\n\t\t\t\t\t\t_this.$emit('middlewareEvent', {});\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t},\t\r\n\t\t\t\tmounted:function(){\r\n\r\n\t\t\t\t\t__callabck = callback(this);\r\n\r\n\t\t\t\t\tstore.setChangeListener(__callback);\r\n\t\t\t\t},\r\n\t\t\t\tbeforeUpdate:function(){\r\n\r\n\r\n\t\t\t\t},\r\n\t\t\t\tdestroyed:function(){\r\n\r\n\t\t\t\t\tstore.unsetChangeListener(__callback);\r\n\t\t\t\t}\r\n\t\t\t};\r\n\r\n\t}, function(component){\r\n\r\n\t\treturn function(actionType, actionKey){\r\n\r\n\t\t\t// used the store change listener for side effects only\r\n\r\n\t\t\tswitch(actionType){\r\n\t\t\t\tcase \"ADD_STUFF\":\r\n\r\n\t\t\t\t\talert(\"Stuff has been added successfully...\");\r\n\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t};\r\n\r\n\t});\r\n\r\n\t// A Vue Component defined with lifecycle hooks via mixins (Radixx store traits)\r\n\r\n\tconst MyVueComponent = {\r\n\t\t\tname:'stuffs', /* The name of the component could be the same as that of the store */\r\n\t\t\tmixins:[trait],\r\n\t\t\tprop:{\r\n\t\t\t\tclaim:{\r\n\t\t\t\t\ttype:String,\r\n\t\t\t\t\trequired:true\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\ttemplate:`\u003cdiv\u003e\r\n\t\t\t\t\t\t\u003cp\u003e{{componentState.profile.fullName}}\u003c/p\u003e\r\n\t\t\t\t\t\t\u003cinput type=\"text\" v-model=\"firstName\"\u003e\r\n\t\t\t\t\t\t\u003cinput type=\"number\" v-model=\"phone\"\u003e\r\n\t\t\t\t\t\t\u003cbutton v-on:click=\"add\" v-bind:disabled=\"buttonDisabled\" v-bind:title=\"gender\"\u003eADD\u003c/button\u003e\r\n\t\t\t\t\t\u003c/div\u003e`,\r\n\t\t\tcomputed:{\r\n\t\t\t\tcomponentState:function(){\r\n\r\n\t\t\t\t\treturn store.getState();\r\n\t\t\t\t\t\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\twatch:{\r\n\t\t\t\tphone:function(newval, oldval){\r\n\r\n\t\t\t\t\t// code goes here... \r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\tdata:function(){\r\n\r\n\t\t\t\t/*\r\n\t\t\t\t\tOnly UI state (transient) should go in here\r\n\r\n\t\t\t\t\tNo core application state should be here\r\n\t\t\t\t*/\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tbuttonDisabled:false,\r\n\t\t\t\t\tfirstName:'',\r\n\t\t\t\t\tphone:''\r\n\t\t\t\t};\r\n\r\n\t\t\t},\r\n\t\t\tmethods:{\r\n\t\t\t\tadd:function(){\r\n\r\n\t\t\t\t\t/* \r\n\t\t\t\t\t\tcalling an action to write data into our store\r\n\t\t\t\t\t\tand trigger an update on the view/store change event, \r\n\t\t\t\t\t\there we are writing to `fullName` with a value of `this.firstName`\r\n\r\n\t\t\t\t\t*/\r\n\r\n\t\t\t\t\taction_creators.addStuff(this.firstName, 'profile');\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t});\r\n\r\n\t/*\r\n\t\tFor communication between one or more components, the props and events of VueJS\r\n\t\tcome in very handy\r\n\t*/\r\n\r\n\t// See: https://vuejs.org/v2/guide/components.html#Composing-Components\r\n\r\n\r\n\tvar app = new Vue({\r\n\t\t\tel:'#app',\r\n\t\t\tcomponents:{\r\n\t\t\t\t'my-component':MyVueComponent\r\n\t\t\t}\r\n\t});\r\n\r\n\r\n```\r\n\r\n```html\r\n\r\n\t\u003cdiv id=\"app\"\u003e\r\n\t\t\u003cmy-component claim=\"basic\"\u003e\u003c/my-component\u003e\r\n\t\u003c/div\u003e\r\n\r\n```\r\n\r\n\u003eAngularJS 1.x\r\n\r\nIf you are using **Radixx** with **AnugularJS / Angular 1.x**, then you need to include the provider made available in this repo (_ng-radixx.js_) and imprt/inject it into the `config` function like as below using '$ngRadixxProvider' name.\r\n\r\n```js\r\n\r\n// Top-level Module { Using the Angular 1.x Provider Helper - $ngRadixx }\r\n\r\nangular.module(\"appy\", [ \r\n\t\t\t'ui.router',\r\n\t\t\t'appy.todos'\r\n])\r\n.config(['$stateProvider', '$urlRouterProvider', '$ngRadixProvider', '$locationProvider',\r\n\tfunction($stateProvider, $urlRouterProvider, $ngRadixxProvider, $locationProvider){\r\n\r\n\t\t$locationProvider.html5Mode(true);\r\n\r\n\t\t$stateProvider\r\n\r\n\t\t.state({\r\n\t\t\turl: '/',\r\n\t\t\ttemplate: '\u003csite\u003e\u003c/site\u003e'\r\n\t\t})\r\n\r\n\t\t.state({\r\n\t\t\turl: '/user',\r\n\t\t\ttemplate: '\u003capp\u003e\u003c/app\u003e',\r\n\t\t\tcontroller:'TodoCtrl'\r\n\t\t});\r\n\r\n\t\t$urlRouterProvider.otherwise('/');\r\n\r\n\t\t$ngRadixxProvider.configure({\r\n\t\t\truntime:{\r\n\t\t\t\tspaMode:false,\r\n\t\t\t\tshutDownHref:'/#/logout'\r\n\t\t\t},\r\n\t\t\tuniversalCoverage:true,\r\n\t\t\tautoRehydrate:false,\r\n\t\t\tpersistenceEnabled:true\r\n\t\t});\r\n\r\n\t\t$ngRadixxProvider.attachMiddleware(function(next, action, prevState){\r\n\t\t\t// ....\r\n\t\t});\r\n});\r\n\r\n\r\n// Domain-level Module\r\n\r\nangular.module(\"appy.todos\", [\r\n\t\t\t'ngRadixx',\r\n\t\t\t'ngSanitize',\r\n\t\t\t'pouchdb' /* using [angular-pouchdb] module */\r\n])\r\n\r\n.factory(\"$todoAction\", ['$ngRadixx',\r\n\r\n\tfunction($ngRadixx){\r\n\r\n\tvar action_c_mappings = {\r\n\t\t'loadTodos':{\r\n\t\t\ttype:'LOAD_TODOS',\r\n\t\t\tactionDefinition:Radixx.Payload.type.array\r\n\t\t},\r\n\t\t'addTodo':{\r\n\t\t\ttype:'ADD_TODO',\r\n\t\t\tactionDefinition:Radixx.Payload.type.object\r\n\t\t},\r\n\t\t'removeTodo':{\r\n\t\t\ttype:'REMOVE_TODO',\r\n\t\t\tactionDefinition:Radixx.Payload.type.numeric.Int\r\n\t\t}\r\n\t};\r\n\t\r\n\t/* create an action object with all necessary action names */\r\n\r\n\treturn $ngRadixx.makeActionCreators(\r\n\t\taction_c_mappings\r\n\t);\r\n\r\n}]) \r\n\r\n.factory(\"$todoStore\", ['$ngRadixx',\r\n\r\n\tfunction($ngRadixx){\r\n\t\r\n\treturn $ngRadixx.makeStore(\r\n\t\t'todos',\r\n\t\tregister,\r\n\t\t[]\r\n\t);\r\n\r\n\tfunction register(action, state){\r\n\t\t\r\n\t\tvar todos = state;\r\n\r\n\t\tswitch(action.actionType){\r\n\t\t\tcase 'ADD_TODO':\r\n\t\t\t\ttodos.push(action.actionData);\r\n\t\t\tbreak;\r\n\t\t\tcase 'LOAD_TODOS':\r\n\t\t\t\ttodos = state.concat.apply(action.actionData);\r\n\t\t\tbreak;\r\n\t\t\tcase 'REMOVE_TODO':\r\n\t\t\t\ttodos.splice(action.actionData, 1);\r\n\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\treturn null;\r\n\t\t\tbreak;\r\n\t\t};\r\n\r\n\t\treturn todos;\r\n\t};\r\n\r\n}]); \r\n\r\n.factory('$fetch', ['$http', '$q', 'pouchDB', function($http, $q, pouchDB){\r\n\t\r\n\treturn: {\r\n\t\tgetTodos:function(url){\r\n\t\t\tvar localdb = pouchDB('todos');\r\n\t\t\treturn localdb.allDocs({\r\n\t\t\t\tinclude_docs:true,\r\n\t\t\t\tattachments:true\r\n\t\t\t}).then(function(res){\r\n\t\t\t\treturn res.rows.map(function(row){\r\n\t\t\t\t\treturn row.doc;\r\n\t\t\t\t});\r\n\t\t\t}).catch(function(err){\r\n\t\t\t\tif(err.name != 'conflict'){ \r\n\t\t\t\t\treturn $http.get(url);\r\n\t\t\t\t}else{\r\n\t\t\t\t\tif(err.name == 'not_found'){\r\n\t\t\t\t\t\treturn [err];\r\n\t\t\t\t\t}\t\r\n\t\t\t\t}\r\n\t\t\t});\r\n\t\t}\t\r\n\t}\r\n}]);\r\n\r\n.controller(\"TodoCtrl\", ['$scope', '$todoAction', '$todoStore', 'pouchDB', '$fetch'\r\n\tfunction($scope, $todoActions, $todoStore, pouchDB, $fetch){\r\n\r\n\t\t// get the 'title' for the {todos} store (a part of the application state)\r\n\t\tvar title = $todoStore.getTitle(), \r\n\r\n\t\tlisten = (function(db){\r\n\r\n\t\t\t\treturn function(storeTitle, actionKey){\r\n\r\n\t\t\t\t\tvar data = this.getState();\r\n\t\t\t\t\tdata._id = (new Date*1);\r\n\t\t\t\t\t\r\n\t\t\t\t\t/* store change listeners stores data in local DB */\t\r\n\t\t\t\t\tdb.put(data) \r\n\t\t\t\t\t.then(function(res){\r\n\r\n\t\t\t\t\t\t/* Assuming to use the Pusher Realtime backend service */ \r\n\t\t\t\t\t\tif(res.ok){\r\n\t\t\t\t\t\t\treturn $http({\r\n\t\t\t\t\t\t\t\turl:'http://localhost:3248/broadcast/pusher',\r\n\t\t\t\t\t\t\t\tmethod:'POST',\r\n\t\t\t\t\t\t\t\tdata:data\r\n\t\t\t\t\t\t\t}); \r\n\t\t\t\t\t\t}else{\r\n\t\t\t\t\t\t\tthrow new Error(\"Not OK =\u003e id:\"+res.id+\", rev:\"+res.rev);\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t})\r\n\t\t\t\t\t.catch(function(err){ \r\n\t\t\t\t\t\t\tconsole.log('App Error: ', err);\r\n\t\t\t\t\t});\r\n\t\t\t\t};\r\n\r\n\t\t}(pouchDB('todos')));\r\n\r\n\r\n\t\t/* loading todos from server DB (or local DB) */\r\n\t\t$fetch.getTodos('http://localhost:4002/todos/all').then(function(data){\r\n\r\n\t\t\t/* \r\n\t\t\t\tcalling hydrate() on a store affects only that store as\r\n\t\t\t\topposed to triggering an action which affects all stores\r\n\r\n\t\t\t\tHere: we're triggering an action\r\n\t\t\t*/\r\n\r\n\t\t\t// $todoStore.hydrate(data.response);\r\n\t\t\t$todoAction.loadTodos(data.response);\r\n\t\t});\r\n\r\n\t\t$scope.$on('locationChangeSuccess', function(event, data){\r\n\r\n\t\t\t/* subscribe store change listener */\r\n\t\t\t$todoStore.setChangeListener(listen);\r\n\t\t});\r\n\r\n\t\t$scope.$on('$unload', function(event, end){\r\n\r\n\t\t\tif($scope.todos.isSavedToPouchDB){\r\n\t\t\t\t// do something with PouchDB\r\n\t\t\t\t\r\n\t\t\t}\t\r\n\r\n\t\t\tevent.preventDefault();\r\n\r\n\t\t});\r\n\r\n\t\t$scope.$on('$radixxDispatch', function(event, appState){\r\n\r\n\t\t\t/* automatically update view when a dispatch happens on the store */\r\n\t\t\t$scope.todos = state[title];\r\n\t\t\tconsole.log(\"on-dispatch: Radixx - \" + JSON.stringify(appState));\r\n\t\t});\r\n\r\n\t\t$scope.$on('$destroy', function(event, data){\r\n\r\n\t\t\t/* unsubscribe store change listener */\r\n\t\t\t$todoStore.unsetChangeListener(listen);\r\n\t\t\r\n\t\t});\r\n\r\n\t\t$scope.addTodo = function(todo){\r\n\r\n\t\t\t$scope.todos.isSavedToPouchDB = false;\r\n\t\t\t/* this function is triggered by a button click  \r\n\t\t\t\ton the view and calls an action creator */\r\n\t\t\t$todoAction.addTodo(todo);\r\n\t\t};\r\n\r\n\t\t$scope.undo = function(){\r\n\r\n\t\t\t$todoStore.undo();\r\n\t\t};\r\n\r\n\t\t$scope.redo = function(){\r\n\r\n\t\t\t$todoStore.redo();\r\n\t\t};\r\n\r\n\t\t$scope.removeTodo = function(todoId){\r\n\r\n\t\t\t/* this function is triggered by a click on the view \r\n\t\t\t\tand calls an action creator */\r\n\t\t\t$todoAction.removeTodo(todoId);\r\n\t\t};\r\n\r\n}]).run(['$rootScope', '$window', '$ngRadixx', function($rootScope, $window, $ngRadixx){\r\n\r\n\t\t$ngRadixx.attachMiddleware(function(next, action, prevState){\r\n\r\n\t\t\tvar nextState = next(\r\n\t\t\t\taction,\r\n\t\t\t\tprevState\r\n\t\t\t);\r\n\t\t\t\r\n\t\t\t// you can use the $rootScope to emit event here too...\r\n\r\n\t\t});\r\n\t\t\r\n\t\t$window.onunload = function(e){\r\n\t\t\r\n\t\t\t$rootScope.$emit('$unload', null);\r\n\t\t\t\r\n\t\t};\r\n\t\r\n\t\tconsole.log(\"app is good to go!\");\r\n\r\n}]);\r\n```\r\n\u003eReactJS \r\n\r\n```html\r\n\r\n\t\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/react@0.13.1/dist/react-with-addons.js\"\u003e\u003c/script\u003e\r\n\r\n\t\u003cscript type=\"text/javascript\" src=\"https://unpkg.com/react@0.13.1/dist/JSXTransformer.js\"\u003e\u003c/script\u003e\r\n```\r\n\r\n![react radixx](React_Radixx.PNG)\r\n\r\n```js\r\n\t\r\n\t/* ====================  */\r\n\r\n\r\n \t/*!\r\n \t *When using Radixx with ReactJS, There are few things you should be aware of.\r\n \t * \r\n \t *\r\n \t * ReactJS and Radixx are not opinionated. This can make it tricky\r\n \t * to use the two together and you can get it all wrong very easily.\r\n \t *\r\n \t * Using the concept of Root, Presentation and Container components - adapted from Dan Abrahamov (creator of Redux)\r\n \t * it is possible to get it right when using ReactJS with as many as 12 components in a project. If you have at most\r\n \t * 2 components in your project then you need not use Radixx or if you do you don't really need to organize your\r\n \t * components into ROOT, PRESENTATION and CONTAINER components. Below is a list of guidelines to safely use Radixx\r\n \t * with ReactJS\r\n \t *\r\n \t *\t# Only use `setState` on the ROOT and PRESENTATION Components\r\n \t *\t# Only use `componentWillMount` on the ROOT Component (There MUST BE only 1 ROOT Component per App).\r\n \t * \t# Only use `shouldComponentUpdate`, `componentWillRecieveProps`, `componentDidMount`, `componentWillUnmount`, `getDefaultProps` and `mixins` on CONTAINER Components\r\n \t * \t# Only use `shouldComponentUpdate`, `componentWillRecieveProps`, `componentDidUpdate`, `getDefaultProps` `getInitialState` and `mixins` on PRESENTATION Components\r\n \t * \t# The STORE should only be attached to CONTAINER Components\r\n \t * \t# The ACTION_CREATOR should only be attached to PRESENTATION Components\r\n \t *\t# The `render` method can be used on all 3 Component Categories (ROOT, PRESENTATION and CONTAINER)\r\n \t *\t# Only use `makeTrait` method of STORE(s) to create Mixins for the CONTAINER and PRESENTATON Components\r\n \t * \t# Only insert UI state (which is transient and non-persistent) in the state object of PRESENTATION Components\r\n \t *\t# The `setState` method MUST NEVER be used in CONTAINER Components\r\n\t *\r\n\t * You can also do without CONTAINER components and have just ROOT and PRESENTATION componets for a simpler web application\r\n \t *\r\n \t */\r\n\r\n \t/* ===================== */ \r\n\r\n\t/* The code belwo uses ReactJS v0.13.1 - */\r\n\r\n\t// Asuuming to use socket.io (client-side)\r\n\tvar socket = io.connect(\r\n\t\t\t\t\t'http://locahost:8005/websockets', \r\n\t\t\t\t\t{\r\n\t\t\t\t\t\ttimeout:300000, \r\n\t\t\t\t\t\treconnection:true, \r\n\t\t\t\t\t\ttransports:[\"websockets\"]\r\n\t\t\t\t\t}\r\n\t),\r\n\r\n\t/* Radixx Action Creator */\r\n\t\r\n\tactions = Radixx.makeActionCreators({\r\n\t\t'loadShoes':{\r\n\t\t\t\ttype:'LOAD_SHOES',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.array\r\n\t\t},\r\n\t\t'removeShoe':{\r\n\t\t\t\ttype:'REMOVE_SHOE',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.numeric.Int\r\n\t\t},\r\n\t\t'addShoe':{\r\n\t\t\t\ttype:'ADD_SHOE',\r\n\t\t\t\tactionDefinition:Radixx.Payload.type.object\r\n\t\t}\r\n\t}),\r\n\r\n\t/* Radixx Store */\r\n\r\n\tstore = Radixx.makeStore('shoes', function(action, state){\r\n\t\t\r\n\t\tvar stub = state;\r\n\t\t\r\n\t\tswitch(action.actionType){\r\n\t\t\tcase 'ADD_SHOE':\r\n\t\t\t\tstub.shoes.push(action.actionData);\r\n\t\t\tbreak;\r\n\t\t\tcase 'LOAD_SHOES':\r\n\t\t\t\tstub.shoes = action.actionData;\r\n\t\t\tbreak;\r\n\t\t\tcase 'REMOVE_SHOE':\r\n\t\t\t\tstub.shoes.splice(action.actionData, 1);\r\n\t\t\t\tthis.await([\r\n\t\t\t\t\t'clothes'\r\n\t\t\t\t], function(){\r\n\t\t\t\t\t\r\n\t\t\t\t\t/* assuming: custom pub/sub object for communicating \r\n\t\t\t\t\t\twith other stores (not defined here) */\r\n\t\t\t\t\tE.emit('clothes', stub);\r\n\t\t\t\t});\r\n\t\t\tbreak;\r\n\t\t\tdefault:\r\n\t\t\t\treturn null;\r\n\t\t\tbreak;\r\n\t\t}\r\n\r\n\t\treturn stub;\r\n\r\n\t}, {shoes:[],jewellry:[]});\r\n\t\t\t\r\n\r\n\t/* ReactJS - Presentation Component Mixin */\r\n\r\n\tconst PresentationTrait = store.makeTrait(function(store){\r\n\t\r\n\t\treturn {\r\n\t\t\tcomponentWillRecieveProps:function(nextProps){\r\n\t\t\t\tif(nextProps.shoes.length === 15){\r\n\t\t\t\t\tif(this.state.canAddMoreShoes === true){\r\n\t\t\t\t\t\tthis.setState({\r\n\t\t\t\t\t\t\tcanAddMoreShoes:false\r\n\t\t\t\t\t\t}); // set state to something else...\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\tshouldComponentUpdate:function(nextProps, nextState){\r\n\t\t\t\treturn (\r\n\t\t\t\t\t!Radixx.Helpers.isEqual(this.props, nextProps) ||\r\n\t\t\t\t\t!Radixx.Helpers.isEqual(this.state, nextState)\r\n\t\t\t\t);\r\n\t\t\t},\r\n\t\t\tcomponentDidUpdate:function(){\r\n\t\t\t\t\r\n\t\t\t\tsocket.emit('shoe-okay', this.props.shoes);\r\n\t\t\t},\r\n\t\t\t_post:function(_url, _data){\r\n\t\t\t\t\t\r\n\t\t\t\t\t/* Assuming `Axios` is loaded in - return the promise */\r\n\r\n\t\t\t\t\treturn axios.post(_url, _data);\r\n\r\n\t\t\t},\r\n\t\t\tgetInitialState:function(){\r\n\r\n\t\t\t\t/* \r\n\t\t\t\t\tWhen using `getInitialState()`, never return Radixx store state from here. \r\n\t\t\t\t\tyou can use this to hold UI state (which should NEVER go into)\r\n\t\t\t\t */\r\n\r\n\t\t\t\treturn { // UI state ONLY\r\n\t\t\t\t\taddingShoe:false,\r\n\t\t\t\t\tcanAddMoreShoes:true,\r\n\t\t\t\t\tloadingShoes:false\r\n\t\t\t\t};\r\n\t\t\t}\t\r\n\t\t};\r\n\r\n\t});\r\n\r\n\t/* ReactJS - Container Component Mixin */\r\n\r\n\tconst ContainerTrait = store.makeTrait(function(store, listener){\r\n\r\n\t\t\treturn {\r\n\t\t\t\tcomponentWillUnmount:function(){\r\n\r\n\t\t\t\t\t/* unsubscribe store change listener */\r\n\t\t\t\t\tstore.unsetChangeListener(listener);\r\n\t\t\t\t},\r\n\t\t\t\tshouldComponentUpdate:function(nextProps, nextState){\r\n\t\t\t\t\tlet title = store.getTitle();\r\n\t\t\t\t\r\n\t\t\t\t\treturn (\r\n\t\t\t\t\t\t!Radixx.Helpers.isEqual(this.props[title], nextProps[title]) ||\r\n\t\t\t\t\t\t!Radixx.Helpers.isEqual(this.state, nextState)\r\n\t\t\t\t\t);\r\n\t\t\t\t},\r\n\t\t\t\tcomponentDidMount:function(){\r\n\r\n\t\t\t\t\t/* Assuming use of socket.io library - joining a room */\r\n\t\t\t\t\tsocket.join('shoelovers');\r\n\r\n\t\t\t\t\t/* subscribe store change listener */\r\n\t\t\t\t\tstore.setChangeListener(listener);\r\n\r\n\t\t\t\t},\r\n\t\t\t\tgetDefaultProps:function(){\r\n\r\n\t\t\t\t\treturn {\r\n\t\t\t\t\t\tshoes:store.getState('shoes')\r\n\t\t\t\t\t};\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t}, function(actionType, actionKey){\r\n\r\n\r\n\t\t\tswitch(actionType){\r\n\r\n\t\t\t\tcase \"ADD_SHOE\":\r\n\t\t\t\t\t\r\n\t\t\t\t\t// make an AJAX request to the server\r\n\t\t\t\t\tthis._post('http://localhost:5600/shoes', this.getState('shoes')).then(function(data){\r\n\t\t\t\t\t\talert(\"Shoe added sucessfully!\");\r\n\t\t\t\t\t});\r\n\t\t\t\tbreak;\r\n\t\t\t};\r\n\r\n\t});\r\n\r\n\r\n\r\n\r\n\r\n\t/* =============================\r\n\t============================= */\r\n\r\n\r\n\r\n\r\n\t/* PRESENTATION COMPONENT */\r\n\r\n\t// defined with most lifecycle hooks\r\n\t\r\n\t/** @jsx React.DOM */\r\n\r\n\tvar ShoeComponent = React.createClass({\r\n\t\t\tpropTypes:{\r\n\t\t\t\tshoes:React.PropTypes.array\r\n\t\t\t},\r\n\t\t\tgetStyle: function(){\r\n\r\n\t\t\t\treturn {\r\n\t\t\t\t\tdisplay:'block',\r\n\t\t\t\t\twidth:'450px',\r\n\t\t\t\t\tmarginLeft:'auto',\r\n\t\t\t\t\tmarginRight:'auto',\r\n\t\t\t\t\tbackgroundColor:'#ee321f'\r\n\t\t\t\t};\r\n\t\t\t},\r\n\t\t\tonKeys:function(e){\r\n\r\n\t\t\t\tthis.setState((prevState, props) =\u003e {\r\n\t\t\t\t\t\r\n\t\t\t\t\tif(prevState.addingShoe === false){\r\n\t\t\t\t\t\r\n\t\t\t\t\t\treturn {addingShoe:true};\r\n\t\t\t\t\t}\r\n\t\t\t\t\t\r\n\t\t\t\t\treturn prevState;\r\n\t\t\t\t});\r\n\r\n\t\t\t},\r\n\t\t\tonSubmitForm:function(e){\r\n\t\t\t\t\r\n\t\t\t\te.preventDefault();\r\n\r\n\t\t\t\tvar form = e.target;\r\n\r\n\t\t\t\tvar payload = {\r\n\t\t\t\t\tlen:this.props.shoes.length,\r\n\t\t\t\t\tname:form.elements['add'].value\r\n\t\t\t\t};\r\n\r\n\t\t\t\taction.addShoe(payload);\r\n\t\t\t},\r\n\t\t\tmixins:[PresentationTrait],\r\n\t\t\trender:function(){\r\n\r\n\t\t\t\tvar shoes = this.props.shoes.map(function(shoe){\r\n\t\t\t\t\t\t\t\treturn \u003cli id={shoe.type}\u003e{shoe.name}\u003c/li\u003e\r\n\t\t\t\t\t\t\t}), \r\n\r\n\t\t\t\t\t_style = this.getStyle();\r\n\r\n\t\t\t\treturn (\r\n\r\n\t\t\t\t\t\u003csection style={_style}\u003e\r\n\t\t\t\t\t\t\u003cform className=\"form\" onSubmit={this.onSubmitForm}\u003e\r\n\t\t\t\t\t\t\t\u003cinput type=\"text\" name=\"add\" id=\"add\" placeholder=\"Add Shoes...\" onKeyDown={this.onKeys} /\u003e\r\n\t\t\t\t\t\t\t\u003cbutton type=\"submit\" name=\"add\"\u003eADD\u003c/button\u003e\r\n\t\t\t\t\t\t\u003c/form\u003e\r\n\t\t\t\t\t\t\u003cul\u003e\r\n\t\t\t\t\t\t\t{shoes}\r\n\t\t\t\t\t\t\u003c/ul\u003e\r\n\t\t\t\t\t\u003c/section\u003e\r\n\r\n\t\t\t\t);\r\n\t\t\t},\r\n\t\t\t\r\n\t});\r\n\r\n\r\n\r\n\r\n\r\n\r\n\t/* =============================\r\n\t============================= */\r\n\r\n\r\n\r\n\r\n\r\n\t/* CONTAINER COMPONENT */\r\n\r\n\t// defined with no lifecycle hooks\r\n\t\r\n\t/** @jsx React.DOM */\r\n\r\n\tvar ListingsContainer = React.createClass({\r\n\t\t\tmixins:[ContainerTrait],\r\n\t\t\trender:function(){\r\n\r\n\t\t\t\t\t var _shoes = this.props.shoes;\r\n\r\n\t\t\t\t\t return (\r\n\r\n\t\t\t\t\t \t\u003cShoeComponent shoes={_shoes} /\u003e\r\n\r\n\t\t\t\t\t);\r\n\t\t\t}\r\n\t});\r\n\r\n\r\n\r\n\r\n\r\n\r\n\t/* =============================\r\n\t============================= */\r\n\r\n\r\n\r\n\r\n\r\n\r\n\t/* ROOT  COMPONENT */\r\n\r\n\t// defined with fewer lifecycle hooks\r\n\r\n\tvar TheApp = React.createClass({\r\n\t\t\t_loadAppData:function(){\r\n\t\t\t\t\r\n\t\t\t\tif(!Radixx.isAppStateAutoRehydrated()){\r\n\r\n\t\t\t\t\t/* Assuming `Axios` is loaded in - return the promise */\r\n\r\n\t\t\t\t\taxios.get(\"https://getalldata.example.com\").then(function(response){\r\n\t\t\t\t\t\t\r\n\t\t\t\t\t\tRadixx.eachStore(function(store, next){\r\n\r\n\t\t\t\t\t\t\tvar state = store.getState(), title = \"\";\r\n\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tif(Object.isEmpty(state)){\r\n\r\n\t\t\t\t\t\t\t\ttitle = store.getTitle();\r\n\r\n\t\t\t\t\t\t\t\tstore.hydrate(response[title]);\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\t\t\r\n\t\t\t\t\t\t\tnext();\r\n\t\t\t\t\t\t});\r\n\r\n\t\t\t\t\t}).catch(function(error){\r\n\t\t\t\t\t\tconsole.log(error);\r\n\t\t\t\t\t});\r\n\t\t\t\t}\r\n\t\t\t},\r\n\t\t\tcomponentWillMount:function(){\r\n\r\n\t\t\t\t\t/* Configure Radixx */\r\n\r\n\t\t\t\t\tRadixx.configure({\r\n\t\t\t\t\t\tuniversalCoverage:true,\r\n\t\t\t\t\t\tautoRehydrate:true\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\t/* always listen for dispatches */\r\n\r\n\t\t\t\t\tlet this = that;\r\n\r\n\t\t\t\t\tRadixx.onDispatch(function(appstate){\r\n\r\n\t\t\t\t\t\tconsole.log(appstate);\r\n\r\n\t\t\t\t\t\tthat.setState(appsate);\r\n\r\n\t\t\t\t\t});\r\n\r\n\t\t\t\t\tthat._loadAppData();\r\n\r\n\t\t\t\t\t\r\n\t\t\t},\r\n\t\t\trender:function(){\r\n\t\t\t\r\n\t\t\t\tlet shoes = this.state.shoes || {};\r\n\r\n\t\t\t\treturn \u003cListingsContainer shoes={shoes} /\u003e\r\n\t\t\t}\r\n\t});\r\n\r\n\tReactDOM.render(\u003cTheApp /\u003e, document.body, function(){\r\n\t\t\r\n\t\tconsole.log(\"app is good to go!\");\r\n\t\r\n\t});\r\n\t\r\n```\r\n\r\n## Browser Support\r\n\r\n- IE 9.0+ (Trident)\r\n- Edge 13+ (EdgeHTML)\r\n- Opera 11.6+ (Presto, Blink)\r\n- Chrome 4.0+ (Webkit, Blink)\r\n- Firefox 4.0+ (Gecko)\r\n- Safari 7.0+ (AppleWebkit)\r\n\r\n## Gotchas/Caveats\r\n\r\n- Radixx would not maintain state across multiple tabs in a browser except the  **\u003cq\u003euniversalCoverage\u003c/q\u003e** config option is set to **true**.\r\n\r\n- Radixx does provide for extrememly old browsers like (IE 8) but will not work properly on these because all polyfills for **ES5 native browser APIs** (e.g. Array.prototype.forEach / Array.prototype.reduceRight / Object.keys / Object.create) have been removed from source as from v0.1.3. Please, include the [polyfill.io library](https://polyfill.io) if you wish to still support IE 8 fully.\r\n\r\n- Radixx throws an error if the **\u003cq\u003eruntimeMode\u003c/q\u003e** config option is omitted.\r\n\r\n- Radixx would not access the storage objects where necessary if it is placed in an iframe that has very tight _sandbox attribute_ restrictions or does not have the same scheme and domain as the parent window (same-origin policy restrictions) except a reverse proxy tactic or the [domain hack](http://qnimate.com/same-origin-policy-in-nutshell/) is used.\r\n\r\n- Trying to `swap` the store callback using the **swapCallback()** method before setting a store listener using **setChangeListener()** will always throw a type error.\r\n\r\n- Whenever you instantly fill up a store by using the **hydrate()** method, the store callback is NEVER called. Only middlewares and store state change listeners are called. Also, the data in the store is OVERWRITTEN with that passed as an argument to the **hydrate()** method.\r\n\r\n- It's COMPULSORY to call **Radixx.configure()** before your application code loads (after loading the Radixx library) else **Radixx** may not work properly or as expected. If you would like to setup **Radixx** with the default config simply call as below: \r\n\r\n```js\r\n\r\n\t.\r\n\t.\r\n\t.\r\n\r\n\tRadixx.configure({\r\n\r\n\t});\r\n```\r\n\r\n## License\r\n\r\nMIT\r\n\r\n## Live Projects { that use _Radixx_ for state management in production }\r\n\r\n- [**NTI Portal**](https://my.nti.edu.ng/applicants/apply) - National Teachers Institute Kaduna - Applicants Section\r\n- [**Stitch NG**](https://www.stitch.ng) -  Fashion Technology Product\r\n\r\n## Contributing\r\n\r\nPlease feel free to open an issue, fix a bug or send in a pull request. I'll be very glad you did. You can also reach me on Twitter [@isocroft](https://twitter.com/isocroft). See the **CONTRIBUTING.md** file for more details.\r\n\r\n[npm-url]: https://npmjs.com/package/radixx\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisocroft%2Fradixx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fisocroft%2Fradixx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fisocroft%2Fradixx/lists"}