{"id":13499724,"url":"https://github.com/moreartyjs/moreartyjs","last_synced_at":"2025-10-22T18:18:04.616Z","repository":{"id":57302846,"uuid":"21702863","full_name":"moreartyjs/moreartyjs","owner":"moreartyjs","description":"Morearty.js - centralized state management for React in pure JavaScript","archived":true,"fork":false,"pushed_at":"2017-12-06T00:06:18.000Z","size":2416,"stargazers_count":669,"open_issues_count":12,"forks_count":41,"subscribers_count":26,"default_branch":"master","last_synced_at":"2024-10-03T20:46:38.209Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moreartyjs.png","metadata":{"files":{"readme":"README.md","changelog":null,"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":"2014-07-10T17:05:52.000Z","updated_at":"2024-05-22T19:58:42.000Z","dependencies_parsed_at":"2022-09-11T03:52:31.732Z","dependency_job_id":null,"html_url":"https://github.com/moreartyjs/moreartyjs","commit_stats":null,"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moreartyjs%2Fmoreartyjs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moreartyjs%2Fmoreartyjs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moreartyjs%2Fmoreartyjs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moreartyjs%2Fmoreartyjs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moreartyjs","download_url":"https://codeload.github.com/moreartyjs/moreartyjs/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222465627,"owners_count":16989050,"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":[],"created_at":"2024-07-31T22:00:39.456Z","updated_at":"2025-10-22T18:17:59.310Z","avatar_url":"https://github.com/moreartyjs.png","language":"JavaScript","readme":"[npm-url]: https://npmjs.org/package/morearty\n[npm-image]: http://img.shields.io/npm/v/morearty.svg?style=flat\n\n# Morearty.js\n[![NPM version][npm-image]][npm-url] [![Build Status](https://travis-ci.org/moreartyjs/moreartyjs.svg?branch=master)](https://travis-ci.org/moreartyjs/moreartyjs) [![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/moreartyjs/moreartyjs?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n[![devDependency Status](https://david-dm.org/moreartyjs/moreartyjs/dev-status.svg)](https://david-dm.org/moreartyjs/moreartyjs#info=devDependencies)\n\n* [Introduction](#introduction)\n* [Download](#download)\n* [Dependencies](#dependencies)\n* [Current status](#current-status)\n* [Documentation](#documentation)\n* [Usage](#usage)\n* [TodoMVC](#todomvc)\n  * [App component](#app-component)\n  * [Header component](#header-component)\n  * [TodoList component](#todolist-component)\n  * [TodoItem component](#todoitem-component)\n  * [Footer component](#footer-component)\n  * [Starting the application](#starting-the-application)\n  * [Principal differences from raw React](#principal-differences-from-raw-react)\n* [Flux implementation](#flux-implementation)\n* [React Native support](#react-native-support)\n* [Care to help?](#care-to-help)\n  * [Building Morearty](#building-morearty)\n* [Credits](#credits)\n\n# Introduction\n\n**Morearty.js** is a thin layer on top of [React](http://facebook.github.io/react/index.html) (implemented as a mixin) providing better state management facilities in the manner of [Om](https://github.com/swannodette/om) but written in pure JavaScript.\n\nUnderneath, Morearty leverages immutable data structures, provided by Facebook's [Immutable](https://github.com/facebook/immutable-js) library, which hold the state of an application. That state is described by a single [Binding](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html) object and all state transitions are performed through it. When an application component needs to delegate a part of its state to a sub-component, it can create a [sub-binding](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html#sub) which points to a nested location within the global state and is fully synchronized with the original binding. This way every component knows only what it should know and the entire state is effectively encapsulated. Morearty detects any changes automatically and triggers re-rendering. Moreover, each component gets a correctly defined [shouldComponentUpdate](http://facebook.github.io/react/docs/component-specs.html#updating-shouldcomponentupdate) method that compares the component's state using straightforward JavaScript strict equals operator `===`. So, only the components whose state was altered are re-rendered.\n\nMorearty puts state updates in a render queue and applies them asynchronously in [requestAnimationFrame](http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/) in one pass, falling back to `setTimeout` when `requestAnimationFrame` is not available. This dramatically simplifies reasoning about the application and improves performance.\n\nSee [documentation](#documentation) for more info.\n\n# Download\n\nBrowser, AMD, Node.js environments are supported. You can get [production](https://raw.githubusercontent.com/moreartyjs/moreartyjs/master/dist/morearty.min.js) or [development](https://raw.githubusercontent.com/moreartyjs/moreartyjs/master/dist/morearty.js) versions. Or just `npm install morearty`.\n\n# Dependencies\n\nMorearty requires React version 0.13 or higher ([download](http://facebook.github.io/react/downloads.html)) and Immutable 3.7 and above ([download](https://github.com/facebook/immutable-js/releases)). **Both should be available as global variables with names `React` and `Immutable` unless you're using NPM.** Require.js users can do something like:\n\n```javascript\nrequire.config({\n  paths: {\n    react: 'path/to/react',\n    immutable: 'path/to/immutable'\n  }\n});\n\nrequire(['react', 'immutable'], function (React, Imm) {\n  window.React = React;\n  window.Immutable = Imm;\n\n  require(['component/Bootstrap'], function (Bootstrap) {\n    React.renderComponent(\n      Bootstrap(),\n      document.getElementById('root')\n    );\n  });\n});\n```\n\n# Current status\n\n**Morearty** 0.7 series changes:\n\n* Support React 0.14 (starting from `0.7.25`).\n* React Native [support](#react-native-support) (thanks to @gilbox).\n* Support React 0.13 and Immutable 3.7.\n* Asynchronous rendering is the default, synchronous mode is no longer supported.\n* Support [cancelling](https://rawgit.com/moreartyjs/moreartyjs/master/doc/TransactionContext.html#cancel) transactions.\n* Support `this.addBindingListener(...)` in components for component lifecycle bounded listeners creation. Just listen for changes, all required cleanup is performed in `componentWillUnmount` automatically.\n* Support `renderOnce` configuration parameter useful to ensure rendering is performed only once. Other server rendering corrections.\n* `Context.bootstrap` helper method simplifying application bootstrapping.\n* Support dynamic bindings ([#36](https://github.com/moreartyjs/moreartyjs/issues/36)).\n* Support passing custom React context ([#37](https://github.com/moreartyjs/moreartyjs/issues/37)).\n* Introduced [observed bindings](https://github.com/moreartyjs/moreartyjs/wiki/Authoring-components#using-observed-bindings) and [observed props](https://github.com/moreartyjs/moreartyjs/wiki/Authoring-components#using-observed-props).\n* Support IE8. Deprecate `Binding.delete` in favor of `remove`.\n* Support `getDefaultMetaState` in components.\n\n**Morearty** 0.6 series changes:\n\n* React 0.12 and Immutable 3.0 or higher now required.\n* Introduce [bindings meta info](https://github.com/moreartyjs/moreartyjs/wiki/Binding#attaching-meta-information) that allows to store data you don't want to put in the main state, e.g. validation info, history, and so on.\n* Generate less garbage during render.\n* History module migrated on meta binding API.\n\nSee [releases page](https://github.com/moreartyjs/moreartyjs/releases) for detailed per-release changes descriptions.\n\n# Documentation\n\nSee Wiki [pages](https://github.com/moreartyjs/moreartyjs/wiki) for a thourough explanation of Morearty concepts.\n\nAuto-generated API documentation is available [here](https://rawgit.com/moreartyjs/moreartyjs/master/doc/index.html).\n\n# Usage\n\nTo start using Morearty.js add the script to the page or load it with your favorite AMD loader, e.g. [Require.js](http://requirejs.org/), and create Morearty context using [createContext](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Morearty.html#createContext) method:\n\n```javascript\nvar Ctx = Morearty.createContext({\n  initialState: {\n    nowShowing: 'all',\n    items: [{\n      title: 'My first task',\n      completed: false,\n      editing: false\n    }]\n  }\n});\n```\n\nWhen you create components this way, each acquires a correctly defined `shouldComponentUpdate` method which uses that component's binding (if any) to determine if its state was changed. By default, state is transferred to sub-components in the `binding` attribute and can be retrieved using the `getDefaultBinding` method.\n\n# TodoMVC\nTo continue this introduction, [TodoMVC](http://todomvc.com/) implementation based on Morearty.js will be used ([repository](https://github.com/moreartyjs/todomvc-moreartyjs), [application](https://rawgit.com/moreartyjs/todomvc-moreartyjs/master/index.html)). You should have some previous React knowledge to follow painlessly, only Morearty-specific parts will be described.\n\n## App component\nLet's now define main application module `App`:\n\n```javascript\nvar NOW_SHOWING = Object.freeze({ ALL: 'all', ACTIVE: 'active', COMPLETED: 'completed' });\n\nvar App = React.createClass({\n  displayName: 'App',\n\n  mixins: [Morearty.Mixin],\n\n  componentDidMount: function () {\n    var binding = this.getDefaultBinding();\n    Router({\n      '/': binding.set.bind(binding, 'nowShowing', NOW_SHOWING.ALL),\n      '/active': binding.set.bind(binding, 'nowShowing', NOW_SHOWING.ACTIVE),\n      '/completed': binding.set.bind(binding, 'nowShowing', NOW_SHOWING.COMPLETED)\n    }).init();\n  },\n\n  render: function () {\n    var binding = this.getDefaultBinding();\n    return (\n      \u003csection id='todoapp'\u003e\n        \u003cHeader binding={ binding } /\u003e\n        \u003cTodoList binding={ binding } /\u003e\n        \u003cFooter binding={ binding } /\u003e\n      \u003c/section\u003e\n    );\n  }\n});\n```\n\nNotice that `App` uses `getDefaultBinding` method to retrieve its state binding and delegate it to its children. See `getDefaultBinding` API [doc](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Morearty.Mixin.html#getDefaultBinding) for the discussion of the default binding concept.\n\n## Header component\n\n```javascript\nvar Header = React.createClass({\n  displayName: 'Header',\n  mixins: [Morearty.Mixin],\n\n  componentDidMount: function () {\n    this.refs.newTodo.getDOMNode().focus(); // focus on show\n  },\n\n  onAddTodo: function (event) {\n    var title = event.target.value;\n    if (title) {\n      this.getDefaultBinding().update('items', function (todos) { // add new item\n        return todos.push(Immutable.Map({\n          id: currentId++,\n          title: title,\n          completed: false,\n          editing: false\n        }));\n      });\n      event.target.value = '';\n    }\n  },\n\n  render: function () {\n    return (\n      \u003cheader id='header'\u003e\n        \u003ch1\u003etodos\u003c/h1\u003e\n        \u003cMorearty.DOM.input id='new-todo' // // requestAnimationFrame-friendly wrapper around input\n                            ref='newTodo'\n                            placeholder='What needs to be done?'\n                            onKeyDown={ Morearty.Callback.onEnter(this.onAddTodo) } /\u003e\n      \u003c/header\u003e\n    );\n  }\n});\n```\n\nIn `onAddTodo` method component state is [updated](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html#update) by appending new TODO item to the list. `render` method output custom `input` component version suitable for [rendering in requestAnimationFrame](https://github.com/moreartyjs/moreartyjs#requestanimationframe-support).\n\n## TodoList component\n\n```javascript\nvar TodoList = React.createClass({\n  displayName: 'TodoList',\n\n  mixins: [Morearty.Mixin],\n\n  onToggleAll: function (event) {\n    var completed = event.target.checked;\n    this.getDefaultBinding().update('items', function (items) {\n      return items.map(function (item) {\n        return item.set('completed', completed);\n      });\n    });\n  },\n\n  render: function () {\n    var binding = this.getDefaultBinding();\n    var nowShowing = binding.get('nowShowing');\n    var itemsBinding = binding.sub('items');\n    var items = itemsBinding.get();\n\n    var isShown = function (item) {\n      switch (nowShowing) {\n        case NOW_SHOWING.ALL:\n          return true;\n        case NOW_SHOWING.ACTIVE:\n          return !item.get('completed');\n        case NOW_SHOWING.COMPLETED:\n          return item.get('completed');\n      }\n    };\n\n    var renderTodo = function (item, index) {\n      var itemBinding = itemsBinding.sub(index);\n      return isShown(item) ? \u003cTodoItem binding={ itemBinding} key={ itemBinding.toJS('id') } /\u003e : null;\n    };\n\n    var allCompleted = !items.find(function (item) {\n      return !item.get('completed');\n    });\n\n    return (\n      \u003csection id='main'\u003e\n      {\n        items.count() ?\n          \u003cMorearty.DOM.input id='toggle-all'\n                              type='checkbox'\n                              checked={ allCompleted }\n                              onChange={ this.onToggleAll } /\u003e :\n          null\n      }\n        \u003cul id='todo-list'\u003e{ items.map(renderTodo).toArray() }\u003c/ul\u003e\n      \u003c/section\u003e\n    );\n  }\n});\n```\n\n`onToggleAll` callback sets `completed` property on all items. Note how state is transferred to the children: only the relevant sub-state is passed using [sub](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html#sub) method which creates a sub-binding pointing deeper into global state. So, TODO item can only access and modify its own cell, and the rest of application state is protected from incidental modification. [val](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html#val) method allows to retrieve the value stored in the binding or in its sub-path.\n\n## TodoItem\n\n```javascript\nvar TodoItem = React.createClass({\n  displayName: 'TodoItem',\n\n  mixins: [Morearty.Mixin],\n\n  componentDidUpdate: function () {\n    var ctx = this.getMoreartyContext();\n    if (ctx.isChanged(this.getDefaultBinding().sub('editing'))) {\n      var node = this.refs.editField.getDOMNode();\n      node.focus();\n      node.setSelectionRange(0, node.value.length);\n    }\n  },\n\n  onToggleCompleted: function (event) {\n    this.getDefaultBinding().set('completed', event.target.checked);\n  },\n\n  onToggleEditing: function (editing) {\n    this.getDefaultBinding().set('editing', editing);\n  },\n\n  onEnter: function (event) {\n    this.getDefaultBinding().atomically()\n      .set('title', event.target.value)\n      .set('editing', false)\n      .commit();\n  },\n\n  render: function () {\n    var binding = this.getDefaultBinding();\n    var item = binding.get();\n\n    var liClass = React.addons.classSet({\n      completed: item.get('completed'),\n      editing: item.get('editing')\n    });\n    var title = item.get('title');\n\n    return (\n      \u003cli className={ liClass }\u003e\n        \u003cdiv className='view'\u003e\n          \u003cMorearty.DOM.input className='toggle'\n                              type='checkbox'\n                              checked={ item.get('completed') }\n                              onChange={ this.onToggleCompleted } /\u003e\n          \u003clabel onClick={ this.onToggleEditing.bind(null, true) }\u003e{ title }\u003c/label\u003e\n          \u003cbutton className='destroy' onClick={ binding.remove.bind(binding, '') }\u003e\u003c/button\u003e\n        \u003c/div\u003e\n        \u003cMorearty.DOM.input className='edit'\n                            ref='editField'\n                            value={ title }\n                            onChange={ Morearty.Callback.set(binding, 'title') }\n                            onKeyDown={ Morearty.Callback.onEnter(this.onEnter) }\n                            onBlur={ this.onToggleEditing.bind(null, false) } /\u003e\n      \u003c/li\u003e\n    );\n  }\n});\n```\n\nHere component title is written to the global state using [set](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Callback.html#set) helper when text in changed. To remove the item no callback needs to be passed from the parent: item component just calls Binding's [remove](https://rawgit.com/moreartyjs/moreartyjs/master/doc/Binding.html#remove) method which removes it from the list of items. In `onEnter` method transaction is used to prevent re-rendering between state transitions. It effectively notifies global listeners once on [commit](https://rawgit.com/moreartyjs/moreartyjs/master/doc/TransactionContext.html#commit).\n\n## Footer component\n\n```javascript\nvar Footer = React.createClass({\n  displayName: 'Footer',\n\n  mixins: [Morearty.Mixin],\n\n  onClearCompleted: function () {\n    this.getDefaultBinding().update('items', function (items) {\n      return items.filter(function (item) {\n        return !item.get('completed');\n      });\n    });\n  },\n\n  render: function () {\n    var binding = this.getDefaultBinding();\n    var nowShowing = binding.get('nowShowing');\n\n    var items = binding.get('items');\n    var completedItemsCount = items.reduce(function (acc, item) {\n      return item.get('completed') ? acc + 1 : acc;\n    }, 0);\n\n    return (\n      \u003cfooter id='footer'\u003e\n        \u003cspan id='todo-count'\u003e{ items.count() - completedItemsCount + ' items left' }\u003c/span\u003e\n        \u003cul id='filters'\u003e\n          \u003cli\u003e\n            \u003ca className={ nowShowing === NOW_SHOWING.ALL ? 'selected' : '' } href='#/'\u003eAll\u003c/a\u003e\n          \u003c/li\u003e\n          \u003cli\u003e\n            \u003ca className={ nowShowing === NOW_SHOWING.ACTIVE ? 'selected' : '' } href='#/active'\u003eActive\u003c/a\u003e\n          \u003c/li\u003e\n          \u003cli\u003e\n            \u003ca className={ nowShowing === NOW_SHOWING.COMPLETED ? 'selected' : '' } href='#/completed'\u003eCompleted\u003c/a\u003e\n          \u003c/li\u003e\n        \u003c/ul\u003e\n      {\n        completedItemsCount ?\n          \u003cbutton id='clear-completed' onClick={ this.onClearCompleted }\u003e\n            { 'Clear completed (' + completedItemsCount + ')' }\n          \u003c/button\u003e :\n          null\n      }\n      \u003c/footer\u003e\n    );\n  }\n});\n```\n\nNothing special here so let's jump straight to...\n\n## Starting the application\n\n```javascript\n\nvar Bootstrap = Ctx.bootstrap(App); // will pass root binding to App\n\nReact.render(\n  \u003cBootstrap /\u003e,\n  document.getElementById('root')\n);\n```\n\n`Ctx.bootstrap` method creates Morearty application bootstrap component which is then passed to React render routine.\n\n## Principal differences from raw React\n\nYou can compare this Morearty-based TodoMVC implementation to the official React [version](https://github.com/tastejs/todomvc/tree/gh-pages/architecture-examples/react). Main highlights are:\n\n* No callbacks are passed to sub-components. This becomes especially useful when you find yourself trying to transfer a callback to a component's grand-children (you may never know how your DOM may be restructured after a redesign). There is nothing inherently wrong in passing callbacks to sub-components, but in many cases this can be avoided.\n* No hacks in code simulating immutable state and other tricks (look at the comments within React version sources).\n* Reasoning about the application is much simpler!\n* Each component gets a `shouldComponentUpdate` method, no need to define it manually (but you can if you like).\n* Less code.\n\n# Flux implementation\n\n[z3tsu](https://github.com/z3tsu) provided Flux version of Todo-MVC based on [Reflux](https://github.com/spoike/refluxjs): [z3tsu/todomvc-morearty-reflux](https://github.com/z3tsu/todomvc-morearty-reflux).\n\n# React Native support\n\nStarting from version *0.7.16* running on React Native is supported:\n\n```javascript\nvar Morearty = require('morearty/native');\n```\n\n# Care to help?\n\nFeel free to [provide](https://github.com/moreartyjs/moreartyjs/issues) ideas, suggestions, enhancements, documentation improvements. Any feedback or input is highly appreciated. Morearty development is currently driven mostly by *feature requests*.\n\n## Building Morearty\n\nMorearty uses NPM scripts for building: `npm run \u003ccommand\u003e` where `command` is one of:\n\n* test (run tests);\n* build (run tests and build dist files);\n* doc (generate documentation).\n\n# Credits\n\n* Alexander Semenov @Tvaroh (author)\n* Marat Bektimirov @mbektimirov (collaborator)\n* Tim Griesser @tgriesser (collaborator)\n* Pavel Birukov @r00ger (collaborator)\n* Gil Birman @gilbox (collaborator)\n* Valentine Valyaeff @valff (collaborator)\n","funding_links":[],"categories":["Awesome React","react","Uncategorized"],"sub_categories":["Tools","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoreartyjs%2Fmoreartyjs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoreartyjs%2Fmoreartyjs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoreartyjs%2Fmoreartyjs/lists"}