{"id":13701329,"url":"https://github.com/radonjs/Radon","last_synced_at":"2025-05-04T21:30:44.989Z","repository":{"id":57343008,"uuid":"148364783","full_name":"radonjs/Radon","owner":"radonjs","description":"Object oriented state management solution for front-end development.","archived":false,"fork":false,"pushed_at":"2018-10-13T22:14:17.000Z","size":282,"stargazers_count":77,"open_issues_count":4,"forks_count":6,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-19T05:57:20.448Z","etag":null,"topics":["async","async-await","asynchronous","frontend-framework","frontend-webdevelopment","object-oriented","object-oriented-programming","predictable","predictable-state","promises","react","state","state-management","time-travel"],"latest_commit_sha":null,"homepage":"http://radonjs.org","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/radonjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-09-11T18:53:27.000Z","updated_at":"2024-07-27T16:06:15.000Z","dependencies_parsed_at":"2022-09-17T06:00:20.368Z","dependency_job_id":null,"html_url":"https://github.com/radonjs/Radon","commit_stats":null,"previous_names":["mad-eye-moody/object-oriented-async-state-management"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radonjs%2FRadon","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radonjs%2FRadon/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radonjs%2FRadon/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/radonjs%2FRadon/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/radonjs","download_url":"https://codeload.github.com/radonjs/Radon/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252403689,"owners_count":21742412,"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":["async","async-await","asynchronous","frontend-framework","frontend-webdevelopment","object-oriented","object-oriented-programming","predictable","predictable-state","promises","react","state","state-management","time-travel"],"created_at":"2024-08-02T20:01:29.805Z","updated_at":"2025-05-04T21:30:44.664Z","avatar_url":"https://github.com/radonjs.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"# \u003cimg src='https://i.imgur.com/k6JIgZR.png' height='130'/\u003e\n\n[![Build Status](https://img.shields.io/travis/com/radonjs/Radon/master.svg?label=Radon\u0026style=flat-square)](https://travis-ci.com/radonjs/Radon)  [![npm](https://img.shields.io/npm/v/radon-js.svg?style=flat-square)](https://npmjs.org/package/radon-js)\n\n[Radon](http://radonjs.org) is an object-oriented state management framework for JavaScript applications.\n\nRead our documentation at [radonjs.org](http://radonjs.org/docs/introduction)\n\n# Why?\n\n## Data Encapsulation\n\nOne of the first goals of Radon was to implement an object oriented state manager capable of data encapsulation. Many state managers allow pieces of state to be accessible by any component or module, and with that access follows modification allowances. This inherently conflicts with a ubiquitous object oriented programming practice: limiting scope. Limiting the scope of a variable or method provides better context for its purpose and makes it easier to reason about. Plus, there's the added bonus of protecting data from being modified by script that has several degrees of separation. Many programming languages have native features to handle data encapsulation such as privatized class attributes and methods. Unfortunately, Javascript doesn't have the same privatization features held by universal languages such as Java and C/C++. Therefore, the data encapsulation feature of Radon needed to be derived by other means.\n\nTo understand encapsulation in Radon, it is first important to understand how the data is organized. Radon is built using a tree data structure. Pieces of state are stored in specially designed nodes and are organized in a way that parallels the component tree of many common frontend frameworks such as React or Vue. For example, if a developer created an initial App component that needed access to variables in state, a corresponding AppState node would be created that contained those specific variables and any accompanying modifier functions. Now let's say the App component renders two more components named Navbar and Main. If Navbar were to be stateful, it would need a corresponding node called NavbarState. If the same thing can be said for Main, then it would have a corresponding state node called MainState. If a frontend component is intended to be stateless, then there will be no corresponding state node. So now we can hopefully start to imagine that the App Component is at the top of a component tree (as the root), with NavbarState and MainState branching beneath it. The same can be said for the State Tree. AppState is our root, with NavbarState and MainState branching below.\n\nBut what does this mean for data encapsulation? The intention for the State Tree is for state nodes to share their data and modifiers with corresponding frontend components. However, this implementation alone would be too constricting of the data. Therefore, frontend components are not only able to access the data from their corresponding state nodes, but also the data from its parent, grandparent, and any further parent tracing back to the root. Now there's a greater sense of flow that encourages commonly used and shared data to be stored near the root, and specialized data to be stored as leaves on the tree. In sum, frontend components will have access to parental lineage data, but will not have access to their sibling’s or children's data. Thus, varying pieces of state are exposed where they are needed, and hidden where they are not.\n\n## Component Rendering Linked to Objects in State\n\nAnother feature of Radon intends to remove unnecessary re-rendering that can emerge from modifying objects in state. In other state management systems, modifying a single key/value pair in a plain object or an index in an array will result in a re-render of any component subscribed to the object. The Radon State Tree solves this problem by deconstructing objects into state nodes by index or key/value pairs. The object deconstruction feature allows for direct modification of these indices/pairs and triggers a re-render of only the component listening to that particular data point.\n\n## Asynchronous Modifications to State\n\nModifiers are functions written by the developer that can only modify a single state variable. Developers have the option to create an asynchronous modifier which may seem problematic if multiple modifiers are called in tandem to edit the same piece of state. However, Radon ensures that all state changes, whether asynchronous or synchronous, occur in the order of initial invocation. This is accomplished with an asynchronous queue that awaits the completion of the most recently invoked modifier before progressing to the next. Hence, the developer does not need to worry about conflicting state changes or out of order updates.\n\n# Getting Started\n\nTo install the stable version using npm as your package manager:\n\n```npm install --save radon-js```\n\nThe Radon source code is transpiled to ES2015 to work in any modern browser. You don't need to use Babel or a module bundler to get started with Radon.\n\nMost likely, you'll also need the React bindings and the developer tools.\n\n```npm install --save react-radon```\n\nUnlike Radon, React doesn't provide UMD builds, so you will need to use a CommonJS module bundler like Webpack, Parcel, or Rollup to utilize Radon with React.\n\n## How Radon Works\n\n```javascript\nimport { StateNode } from 'radon-js'\n\n/*\nStateNode is a class needed for creating instances of state. In Radon, StateNodes are created in\ntandem with frontend components. The naming convention is important here; if you have created\na frontend component called App with the intent of statefulness, then an instance of StateNode must be \ndeclared and labeled as AppState. This will allow the App component to properly bind to AppState\nat compile time.\n\n\nThe new instance of StateNode takes two arguments: the first argument is the name of the StateNode you\nare creating which must follow our naming convention. The second argument is the name of the parent\nnode. One StateNode must be considered the root of the state tree. Therefore, at only one occasion can\nthe parent argument be omitted. This instance of StateNode will be considered the root. Every other\nStateNode must take a parent argument.\n*/\n\nconst AppState = new StateNode('AppState');\n// or\n// const AppState = new StateNode('AppState', 'OtherState');\n\n/*\nTo declare variables in state, the method initializeState must be called which takes an object\nas an argument. The variable names and their data should be listed in the object as key-value pairs.\n*/\n\nAppState.initializeState({\n  name: 'Radon',\n  status: true,\n  arrayOfNames: []\n})\n\n/*\nModifiers are functions that modify a single variable in state. Modifiers are attached to variables by\ncalling the method initializeModifiers which also takes an object as an argument. The keys of the\nargument object must correspond to variables that have already been declared in AppState. The values\nare objects that contain the modifier functions as key-value pairs. There are two types of modifiers\nin Radon. The first type, as seen below, can accept either 1 or 2 arguments. The 'current' argument\nwill automatically be injected with the bound state variable. The 'payload' argument is any data that \ncan be used to modify or replace the 'current' value of state. Even if the current value of state is \nnot used in the modifier, it will still be passed in automatically.\n*/\n\nAppState.initializeModifiers({\n  name: {\n    updateName: (current, payload) =\u003e {\n      return payload;\n    }\n  },\n  status: {\n    toggleStatus: (current) =\u003e {\n      return !current;\n    }\n  }\n})\n\n/*\nIt is important to note that when these modifiers are called from a component, only the payload argument\nmust be passed into the function as Radon will fill the 'current' parameter by default.\n*/\n\n\u003cbutton onClick={() =\u003e this.props.name.updateName('Radon is cool!!!')}\u003eClick Me\u003c/button\u003e\n\u003cbutton onClick={() =\u003e this.props.status.toggleStatus()}\u003eClick Me Too\u003c/button\u003e\n\n/*\nThe second modifier type is what helps Radon eliminate unnecessary re-rendering of frontend components.\nThis modifier type accepts three arguments and is used exclusively with objects. *Note that\ninitializeModifiers should only be called once. It is shown again here for demonstration purposes only*.\n*/\n\nAppState.initializeModifiers({\n  arrayOfNames: {\n    addNameToArray: (current, payload) =\u003e {\n      current.push(payload);\n      return current;\n    },\n    updateAName: (current, index, payload) =\u003e {\n      return payload;\n    }\n  }\n})\n\n/*\nThe modifier addNumberToArray is nothing new. Since the goal of the modifier is to edit the array as a \nwhole, the entire array object is passed into the 'current' parameter. A modifier that edits the array \nwill cause a re-render of any component that subscribes to the array. However, we may have\ncircumstances in which we only want to edit a single index within an array. In this case we create a\nmodifier that accepts an index. The 'current' value will always reflect arrayOfNumbers[index]. This \nwill prevent a re-render of components listening to the entire array, and will instead only re-render\ncomponents listening to the specified index.\n\nAgain, it is important to note that the 'current' parameter will be injected with state automatically.\n*/\n\n\u003cbutton onClick={() =\u003e updateAName(0, 'Hannah')}\u003eEdit an Index!\u003c/button\u003e\n\n/*\nThe same logic applies to plain objects. Instead of passing a numerical index into a modifier, the key \nof a key-value pair can be passed in instead.\n\nObjects can be nested and it is possible to create modifiers for deeply nested objects. Ultimately, the\nmodifier will always be bound to the parent object. However, the key/index parameter will transform into \na longer chain of 'addresses' to tell Radon exactly where the data is stored. For example:\n*/\n\nnames: {\n  first: ['', 'Beth', 'Lisa'],\n  last: {\n    birth: ['Mitchell', 'Sanchez', 'Delaney'],\n    married: ['Mitchell', 'Smith', 'Delaney']\n  }\n}\n\n/*\nTo inject the name 'Hannah' into index 0 of the 'first' array, the specified 'address' would be first_0.\nTo change the value of index 2 of the 'married' array, the specified 'address' would be last_married_2.\n*/\n\n/*\nOnce all StateNodes have been declared, they should be combined in the function combineStateNodes. The\nreturned object is known as the silo.\n*/\n\nimport AppState from './appState';\nimport NarbarState from './navbarState';\nimport mainState from './mainState';\n\nconst silo = combineStateNodes(AppState, NavbarState, MainState);\n\n```\n\n\n### Bind the state\nIn order to use the **Silo** state in a component, it must be passed to the same from the top of the application. \nThat depends on the framework binding.\nBelow you can find a working example of use of **Radon** on **React** via [react-radon](https://github.com/radonjs/React-Radon), the react binding for this library, as an example:\n\n```javascript\nimport {render} from 'react-dom';\nimport {Provider} from 'react-radon';\n\n// Silo from Exported combineNodes from the example before\nimport silo from './localSiloLocation';\n\nrender(\n  \u003cProvider silo={silo}\u003e\n    \u003cApp /\u003e\n  \u003c/Provider\u003e,\n  document.getElementById('root'));\n\n\n// And in the component where you need the piece of state\n\nimport React from 'react';\nimport { bindToSilo } from 'react-radon'\n\nconst ReactComponent = (props) =\u003e {\n  return (\n    \u003cdiv\u003e\n      {props.name}\n    \u003c/div\u003e\n  )\n}\n\nexport default bindToSilo(ReactComponent);\n\n```\n\n## Built With\n\nRollup - Module Bundler\n\nBabel - ES2015 transpiling\n\n## Versioning\n2.0.0 We use SemVer for versioning.\n\n## Authors\nHannah Mitchell,\n\nHayden Fithyan,\n\nJoshua Wright,\n\nNicholas Smith\n\n## License\nThis project is licensed under the MIT License - see the LICENSE.txt file for details\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradonjs%2FRadon","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fradonjs%2FRadon","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fradonjs%2FRadon/lists"}