{"id":13801198,"url":"https://github.com/levand/quiescent","last_synced_at":"2025-10-21T21:05:28.660Z","repository":{"id":13922309,"uuid":"16621691","full_name":"levand/quiescent","owner":"levand","description":"A lightweight ClojureScript abstraction over ReactJS","archived":false,"fork":false,"pushed_at":"2017-12-11T21:04:43.000Z","size":234,"stargazers_count":612,"open_issues_count":4,"forks_count":46,"subscribers_count":22,"default_branch":"release","last_synced_at":"2025-03-20T03:49:51.145Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"epl-1.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/levand.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-02-07T17:18:36.000Z","updated_at":"2025-01-27T07:20:58.000Z","dependencies_parsed_at":"2022-08-25T10:31:12.998Z","dependency_job_id":null,"html_url":"https://github.com/levand/quiescent","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levand%2Fquiescent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levand%2Fquiescent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levand%2Fquiescent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/levand%2Fquiescent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/levand","download_url":"https://codeload.github.com/levand/quiescent/tar.gz/refs/heads/release","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253932775,"owners_count":21986447,"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-08-04T00:01:20.487Z","updated_at":"2025-10-21T21:05:23.627Z","avatar_url":"https://github.com/levand.png","language":"Clojure","funding_links":[],"categories":["Awesome React","Awesome ClojureScript"],"sub_categories":["Tools","[React.js](https://facebook.github.io/react/) Interface"],"readme":"# Quiescent\n\nA lightweight ClojureScript abstraction over ReactJS, emphasizing its\nability to (re)render immutable values efficiently.\n\nAn obligatory [TodoMVC implementation](https://github.com/levand/todomvc/tree/gh-pages/architecture-examples/quiescent) is available.\n\nSee the [documentation](docs.md) for instructions and examples of how\nto use Quiescent.\n\n## Rationale\n\nReactJS is an extremely interesting approach to UI rendering for\nHTML-based web applications. Its core value proposition is to make it\npossible to write one set of UI rendering code that works for both\ninitial creation and any future updates. Updates are extremely\nefficient, because only the minimal set of necessary deltas are\nactually sent to the DOM.\n\nIn other words, React lets you write your UI code *once*, and still\nhave a dynamic, data-driven application. Ideally, it eliminates any\nneed to write explicit UI manipulation code. Instead, you are\nresponsible only for supplying a specification of what the UI should\nlook like, given certain data. The ReactJS implementation takes care\nof all the details of how to morph the existing DOM to correspond to\nchanges in the input data.\n\nQuiescent is intended to expose this feature and *only* this feature\nin a way that is convenient and idiomatic to ClojureScript, while\nremaining highly efficient.\n\nIt has the following design goals:\n\n* **single concern:** Quiescent is designed to do one thing: render and\n   re-render HTML from immutable values. It does not attempt to\n   participate in state management, inform how you organize your\n   application, or force the use of any specific technique for\n   managing state updates. Feel free to use atoms, watchers,\n   core.async, compilation-based models, message-passing, etc. The\n   only requirement is that values passed to Quiescent components are\n   immutable value types.\n\n* **avoid OO idioms:** ReactJS is itself highly object-oriented, with\n   stateful objects that may implement a variety of\n   behaviors. Quiescent provides a purely functional interface,\n   allowing users to construct a ReactJS component tree using only the\n   basic tools of functional programming: function definition and\n   composition.\n\n* **top-down rendering:** All renders and updates are initiated by\n   instructing Quiescent to render a particular value to a particular\n   location in the DOM. Individual tree components do not maintain\n   their own state and do not re-render themselves unless explicitly\n   directed to do so.\n\n   This is intended to emulate classical 'rendering' semantics for 2D\n   or 3D bitmapped applications, where the entirety of a scene is\n   re-drawn from scratch for every frame. Of course, React/Quiescent\n   does not actually do this, for performance reasons, but the fact\n   that it does not is an implementation detail; the conceptual model\n   is the same.\n\n* **leverage immutability:** By assuming that any value provided to a\n   rendered component is immutable, Quiescent can prevent ReactJS from\n   even calculating if it needs to render sub-trees that have not\n   changed from the last time the application was updated. Since\n   equality checks are highly efficient in ClojureScript, large\n   application structures can be re-rendered frequently with almost no\n   performance hit apart from that necessary to re-render leaf nodes\n   that actually did change.\n\n* **compatibility:** Although you will hopefully be able to write the\n   vast majority of your application using Quiescent's model,\n   you can, if necessary, always fall back and use a raw\n   ReactJS component (or, for that matter, a ReactJS component\n   constructed using another ClojureScript interface). This is\n   possible at any level of the rendering component tree.\n\nThese goals differ slightly from other ClojureScript interfaces to\nReact, as described below.\n\n### Comparison with Om\n\n[Om](http://github.com/swannodette/om) is another ClojureScript\ninterface to ReactJS, highly capable and well-designed. It provides\ncategorically more features than Quiescent, at the cost of taking more\ncontrol and specifying more rigidly the way application state is\nmodeled, updated and re-rendered.\n\nThe most important conceptual distinctions are:\n\n* To create an Om component you must implement a protocol; due to its\n  relative lack of capability, Quiescent only requires components to\n  provide a single render function.\n\n* Om controls the primary application state atom and how it is\n  updated. In Quiescent this is entirely the responsibility of the\n  consumer.\n\n* Om explicitly allows components to maintain local state, while\n  Quiescent forbids this. In my opinion the benefits of requiring\n  components to account for local state do not justify the pervasive\n  cost to implementation.\n\n  Specifically, addressing the two reasons David Nolen gives for allowing\n  application state:\n\n  1. _\"Local state pollutes application data.\"_ An alternative view is\n  that all data is application data, whether it is transient or\n  persistent, important or insignificant, wherever it is actually\n  located. There is little harm in including it in the top-level data\n  structure, and if your application does demand a hierarchy of data\n  (persistent vs. transient, etc) for different purposes, it is better\n  to model that relationship explicitly rather than leaving transient\n  data tucked invisibly away in component state.\n\n  2. _\"Local state is always up-to-date.\"_ This is not relevant in\n  Quiescent's render model because Quiescent does not manage state.\n\n* Om components are always aware of their location in the primary\n  application state, via a _cursor_ (a hybrid of functional zippers\n  and lenses). Quiescent components are not. This means that Om\n  components can dispatch updates to \"themselves\", whereas a DOM event\n  handler function attached to a Quiescent component can only effect\n  change by reaching back and doing something to the top-level\n  application state (e.g., by sending a core.async message, swapping the\n  top-level atom, etc).\n\n  This does, somewhat, negate the concept of component modularity;\n  Quiescent's contention is that the benefit of top-down, value-based\n  rendering exceeds that of truly modular components.\n\nUltimately, though, Om is an excellent, well-thought-out library, and\nif your needs or design goals more closely align with its capabilities\nthan with Quiescent, you should absolutely use it.\n\n### Comparison with Reagent\n\n[Reagent](http://holmsand.github.io/reagent/) is another ClojureScript\nwrapper for ReactJS. It is, perhaps, *easier* than either Om or\nQuiescent and certainly the most readable of the three.\n\nThe key differences between Reagent and Quiescent (or Om, for that\nmatter) are:\n\n* Reagent defaults to a\n  [Hiccup](https://github.com/weavejester/hiccup)-like syntax for\n  component definitions.\n\n* Reagent handles updates via a special version of an atom (a\n  *reactive atom* or *ratom*). Whenever a component references a ratom,\n  watches are established such that the component will re-render when\n  the value of the ratom changes. As such, Reagent components are not\n  driven by top-down data or a singular application state, but by\n  whatever ratoms are referenced in their definition.\n\n* Reagent, like Om, maintains full control of the render/re-render cycle.\n\nAlthough I do not have as much first-hand experience with Reagent, it\nseems to be a very convenient approach, and if it meets your needs you\nshould definitely give it a try.\n\n### Implementation\n\nThis section presumes familiarity with how ReactJS works.\n\nIn short, basic Quiescent components implement only two of ReactJS's\ncomponent lifecycle events:\n\n1. _shouldComponentUpdate_ is always predicated exclusively on whether\nthe immutable value passed to the component constructor function has\nchanged. If so, the component re-renders. If not, it doesn't.\n\n2. The implementation of _render_ is provided as a function supplied\nby the user. The output of this render function is presumed to be a\nsingle ReactJS component, as it is in vanilla ReactJS. The function,\nhowever, is passed the immutable value that was used to construct the\nfunction. It is also passed any additional arguments that were\nprovided to the component constructor.\n\n## See Also\n\n* The [ReactJS website](http://facebook.github.io/react/) for\n  information on ReactJS and how it works.\n\n## CHANGE LOG\n\n### 0.3.2\n\n- Upgrade to React 15.1.0 (#58)\n- Ignore `mask` from cljs.core to avoid compiler warning (#57)\n\n### 0.3.1\n\n- Add newly supported SVG tags which React supports (#43)\n\n### 0.3.0\n\n- Upgrade to React 0.14 (#56)\n- Use symbol name for defcomponent as default React component name (#46)\n- Make it easier to idiomatically use React components defined elsewhere\n  (for example, React Bootstrap). (Inspired by #40)\n\n### 0.2.0\n\nWarning: This release contains breaking changes.\n\n- **breaking change**: The primary namespace has been renamed from\n  `quiescent` to `quiescent.core` to avoid a single-segment namespace,\n  which could cause a variety of problems.\n- **breaking change**: Quiescent now includes a transitive dependency\n  to ReactJS using ClojureScript's new, expanded foreign library\n  support\n  (https://github.com/clojure/clojurescript/wiki/Foreign-Dependencies). You\n  do not need to, and should not, include ReactJS either in your\n  ClojureScript preamble nor in your HTML.\n- Support for new versions of ReactJS.\n- The \"wrapper\" functionality is deprecated (see explanation below).\n- Instead, you can now supply explicit lifecycle hooks when defining\n  components.\n- Keys are now supported on custom components.\n- Added support for naming custom components, which will be helpful\n  for debugging (including the debugging browser extensions provided\n  by ReactJS).\n- Added support for ReactJS' animation extensions (http://facebook.github.io/react/docs/animation.html)\n- Added an examples directory\n- Added support for uncontrolled inputs (fixes #32 and #36).\n\n#### motivation for wrapper deprecation\n\nAs it turns out, there is no way in the current model that wrappers\ncan provide the desired functionality in all cases.\n\nA wrapper component can only modify its _own_ lifecycle methods, not\ntruly those of its child. But neither can it access the\n\"shouldComponentUpdate\" of the child - it must have a\n\"shouldComponentUpdate\" that constantly returns true. Therefore, an\n\"onRender\" wrapper would fire *even if* the wrapped component\nexplicitly did not render due to an unchanged value (or otherwise\noverriding \"shouldComponentUpdate\").\n\n### 0.1.2\n\n- Issue #20 - Wrapper components now copy the \"key\" property from their wrappee.\n- Issue #18 - Define a map of generated DOM functions. This useful for programmatically\ngenerated UIs.\n- Issue #16 - Allow specification of multiple lifecycle handler functions \n  using the same wrapper component. See documentation on `quiescent/wrapper`.\n- Project no longer has ReactJS as a leiningen dependency; clients are responsible for\n  obtaining the most recent version of ReactJS and incorporating that in their own projects.\n\n## License\n\nCopyright © 2014-2015 Luke VanderHart\n\nDistributed under the Eclipse Public License (see LICENSE file).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevand%2Fquiescent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flevand%2Fquiescent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flevand%2Fquiescent/lists"}