{"id":14065984,"url":"https://github.com/jankowtf/reactr","last_synced_at":"2026-03-01T21:23:15.171Z","repository":{"id":21130146,"uuid":"24431067","full_name":"jankowtf/reactr","owner":"jankowtf","description":"Reactive object bindings with built-in caching","archived":false,"fork":false,"pushed_at":"2014-12-09T18:10:28.000Z","size":2299,"stargazers_count":12,"open_issues_count":10,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-11-30T10:44:40.973Z","etag":null,"topics":["r","reactive-objects","reactive-programming","reactive-references"],"latest_commit_sha":null,"homepage":null,"language":"R","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jankowtf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-09-24T20:19:41.000Z","updated_at":"2024-08-07T22:55:26.000Z","dependencies_parsed_at":"2022-08-27T00:10:50.723Z","dependency_job_id":null,"html_url":"https://github.com/jankowtf/reactr","commit_stats":null,"previous_names":["jankowtf/reactr"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jankowtf%2Freactr","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jankowtf%2Freactr/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jankowtf%2Freactr/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jankowtf%2Freactr/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jankowtf","download_url":"https://codeload.github.com/jankowtf/reactr/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228052661,"owners_count":17862105,"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":["r","reactive-objects","reactive-programming","reactive-references"],"created_at":"2024-08-13T07:04:53.140Z","updated_at":"2026-03-01T21:23:15.128Z","avatar_url":"https://github.com/jankowtf.png","language":"R","funding_links":[],"categories":["R"],"sub_categories":[],"readme":"reactr\n======\n\nReactive object bindings with built-in caching and push functionality\n\n## Installation \n\n```\nrequire(\"devtools\")\ndevtools::install_github(\"Rappster/conditionr\")\ndevtools::install_github(\"Rappster/yamlr\")\ndevtools::install_github(\"Rappster/typr\")\ndevtools::install_github(\"Rappster/reactr\")\nrequire(\"reactr\")\n```\n## Purpose \n\nThe package aims at contributing to *Reactive Programming* or *Reactivity* in R. \n\nIt allows the specification of **reactive objects** based on reactive expressions that dynamically bind them to other objects. \n    \nThat way, an object `x` can be dynamically observed by `n` other objects. Whenever `x` changes, the `n` variables observing `x` change according to their reactive expressions that defines the actual binding relationship. This is an approach to ensure consistent states of objects and the entire system. You can choose how changes should be propagated throughout the system: following a *pull* or a *push*  principle (see section **Highlighting selected features**).\n\n## Implementations\n\nTwo different reactivity implementations are provided: \n\n1. Implementation that builds on top of [`shiny`](https://github.com/rstudio/shiny) (recommended for **regular reactive scenarios**)\n\n  I tried to build as much on top of existing functionality in order to make this package as compatible as possible with shiny apps. However, I was not able to implement all of my \"wish-list features\" yet that way - so any help is greatly appreciated.\n  \n  Branch `legacy-shinyOld` contains a legacy version on the road to the current implementation that might or might not be of interest to other developers.\n\n2. Custom implementation (legacy but recommended for **bi-directional reactive scenarios**)\n\n  This implementation is older and is much more \"pedestrian\" as it stems from the time where I did not understand very well yet how reactivity is implemented in [`shiny`](https://github.com/rstudio/shiny). However, while it is quite \"custom-made\" in comparision, it does work and on top reveals some of the interesting details that shiny solves in a similar, yet much more elegantly way. Thus I decided to keep it as a reference for myself and other programmers for the time being.\n\n### Aknowledgements\n\nThe package is greatly inspired by reactivity as implemented by the [shiny](http://shiny.rstudio.com) framework. \n\n### Vignettes (not refactored yet!)\n\n- [Bi-Directional Bindings](https://github.com/Rappster/reactr/blob/master/vignettes/bidirectional_bindings.pdf)\n- [Caching](https://github.com/Rappster/reactr/blob/master/vignettes/caching.pdf)\n- [Convenience Functions](https://github.com/Rappster/reactr/blob/master/vignettes/convenience_functions.pdf)\n- [Pushing](https://github.com/Rappster/reactr/blob/master/vignettes/pushing.Rmd)\n- [Reactive References](https://github.com/Rappster/reactr/blob/master/vignettes/reactive_references.pdf)\n- [Relations to Shiny](https://github.com/Rappster/reactr/blob/master/vignettes/relations_to_shiny.Rmd)\n- [Registry](https://github.com/Rappster/reactr/blob/master/vignettes/registry.pdf)\n- [Strictness](https://github.com/Rappster/reactr/blob/master/vignettes/strictness.Rmd)\n\n----------\n\n### Basics\n\n```\nsetShinyReactive(id = \"x_1\", value = 10)\nsetShinyReactive(id = \"x_2\", value = reactiveExpression(x_1 * 2))\n\nx_1\nx_2\n## --\u003e x_1 * 2 = 20\n\n(x_1 \u003c- 20)\nx_2\n## --\u003e x_1 * 2= 40\n\nsetShinyReactive(id = \"root_dir\", value = getwd())\nsetShinyReactive(id = \"sub_dir\", value = reactiveExpression(\n  file.path(root_dir, \"doc\")\n))\n\nroot_dir\nsub_dir\n## --\u003e \"Q:/home/wsp/rapp2/reactr/doc\"\n\nroot_dir \u003c- tempdir()\nsub_dir\n## --\u003e \"C:\\\\Users\\\\jat\\\\AppData\\\\Local\\\\Temp\\\\RtmpyUOCMk/doc\"\n```\n\nClean up \n\n```\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n\n### Highlighting selected features\n\n1. Strictness levels can be defined for \n\n  - the *creation process* itself in `setReactive()` and`setShinyReactive()`: see argument `strict`\n  - *getting* the visible value of a reactive object: see argument `strict_get`\n  - *setting* the visible value of a reactive object: see argument `strict_set`\n  \n  See vignette [Strictness](https://github.com/Rappster/reactr/blob/master/vignettes/strictness.Rmd) for details.\n    \n2. **Propagation of changes**: you can choose between a **pull** and a **push** paradigm with respect to how changes are propagated throughout the system. \n\n  When using a *pull* paradigm (the default), objects referencing other objects that have changed are not informed of these change until they are explicitly requested (by `get()` or its syntactical sugars).\n  \n  When using a *push* paradigm, an object that changed informs all objects that have a reference to it about the change by implicitly calling the `$getVisible()` method of all of their registered push references. \n  \n  See vignette [Pushing](https://github.com/Rappster/reactr/blob/master/vignettes/pushing.Rmd) for details on this.\n\n3. **Relations to shiny**: as already mentioned, the package has a lot of relations to the [shiny framework](http://shiny.rstudio.com) and thus the actual [shiny](http://cran.r-project.org/web/packages/shiny/index.html) package\n\n  Summary of the added functionality compared to what is currently offered by existing shiny functionality (shiny's limitations should always be read \"AFAIK\" ;-)):\n  \n  1. The same function can be used for setting both reative sources and observers.\n  \n  2. Reactive expressions/binding functions are **hidden** from the user.\n  \n    To the user, all reactive objects appear and behave as if they are actual *non-reactive*/*non-functional* values. This eliminates the need to distinguish (mentally and by code) if a certain value is a *non-functional* value or a *function* that needs to be executed via `()`. \n    \n    The latter is what is necessary when using current shiny functionality based on `shiny::makeReactiveBinding()` and `shiny::reactive()`).\n    \n    However, you can mimick the default shiny behavior by setting `lazy = TRUE`.\n  \n  3. Push updates\n  \n    While shiny implements reactivity following a **pull paradigm** with respect to the way that changes are propagated throughout the system (resembles *lazy evaluation*), `reactr` also offers the alternative use of a **push paradigm** where changes are *actively* propagated via the argument `push = TRUE`.\n    \n  See vignette [Relations to Shiny](https://github.com/Rappster/reactr/blob/master/vignettes/relations_to_shiny.Rmd) for more details.\n\n5. **Reference specification** (only relevant for `setReactive()`):\n\n  The preferred way to specify the reference is via [YAML](http://www.yaml.org/) markup as in the example above. However, there also exist two other ways to specify references.: \n\n  1. Via a function argument `refs`.\n  2. Via explicit `get()` calls in the body of form \n    \n    ```\n    .ref_{number} \u003c- get({id}, {where})\n    ```\n  \n    with `{number}` being an arbitrary number or other symbol, `{id}` being the referenced object's name/ID and `{where}` being the environment where the value belonging to `{id}` was assigned to (e.g. `.ref_1 \u003c- get{\"x_1\", where_1}`).\n\n  See vignette [Reactive References](https://github.com/Rappster/reactr/blob/master/vignettes/reactive_references.Rmd) for details.\n\n6. **Caching mechanism** (only relevant for `setReactive()`): \n\n  Binding functions are only executed if they need to be, i.e. only if one of the referenced objects has actually changed. \n\n  Otherwise a cached value that has been stored from the last update run is returned.\n\n  While this may cost more than it actually helps in scenarios where the binding functions are quite simple and thus don't take long to run, caching *may* reduce runtimes/computation times in case of either more complex and long-running binding functions or when greater amounts of data comes into play (needs to be tested yet). \n  \n  See vignette [Caching](https://github.com/Rappster/reactr/blob/master/vignettes/caching.Rmd) for details.\n\n-----\n\n### Feature showcase: typed sources\n\n```\n## Basics //\n## Strict = 0:\nsetShinyReactive(id = \"x_1\", value = 10, typed = TRUE)\nx_1 \u003c- \"hello world!\"\nx_1\n## --\u003e simply ignored \n\n## Strict = 1:\nsetShinyReactive(id = \"x_1\", value = 10, typed = TRUE, strict = 1)\ntry(x_1 \u003c- \"hello world!\")\nx_1\n## --\u003e ignored with warning\n\n## Strict = 2:\nsetShinyReactive(id = \"x_1\", value = 10, typed = TRUE, strict = 2)\ntry(x_1 \u003c- \"hello world!\")\nx_1\n## --\u003e ignored with error\n\n## Advanced //\nsetShinyReactive(id = \"x_1\", typed = TRUE, from_null = FALSE, strict = 2)\ntry(x_1 \u003c- \"hello world!\")\n\nsetShinyReactive(id = \"x_1\", value = 10, typed = TRUE, to_null = FALSE, strict = 2)\ntry(x_1 \u003c- NULL)\n\nsetShinyReactive(id = \"x_1\", value = 10, typed = TRUE, numint = FALSE, strict = 2)\ntry(x_1 \u003c- as.integer(10))\n```\n\n### Feature showcase 2: pushing\n\nSuch a construct could be used for logging or ensuring that certain database operations are triggered right away after the system state has changed:\n\n```\nsetShinyReactive(id = \"x_1\", value = 10)\nsetShinyReactive(\n  id = \"x_2\", \n  value = reactiveExpression({\n    message(paste0(\"[\", Sys.time(), \"] I'm x_2 and the value of x_1 is: \", x_1))\n    x_1 * 2\n  }), \n  push = TRUE\n)\n## --\u003e [2014-11-13 17:35:11] I'm x_2 and the value of x_1 is: 10\n\nx_1\n## --\u003e 10\n\nx_2\n## --\u003e 20\n```\n\nNote that we never request the value of `x_2` explicitly yet changes in `x_1` are actively pushed to `x_2` thus executing its reactive binding function:\n\n```\n(x_1 \u003c- 11)\n## --\u003e [2014-11-13 17:35:47] I'm x_2 and the value of x_1 is: 11\n## --\u003e 11\n\n(x_1 \u003c- 12)\n## --\u003e [2014-11-13 17:36:14] I'm x_2 and the value of x_1 is: 12\n## --\u003e 12\n\n(x_1 \u003c- 13)\n## --\u003e [2014-11-13 17:36:32] I'm x_2 and the value of x_1 is: 13\n## --\u003e 13\n\nx_2\n## --\u003e 26\n```\n\nClean up \n\n```\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n### Feature showcase 3: getting closer to an actual use case\n\nSpecify reactive objects:\n\n```\nsetShinyReactive(id = \"x_1\", value = 1:5, typed = TRUE)\nsetShinyReactive(id = \"x_2\", value = reactiveExpression(x_1 * 2), typed = TRUE)\n\nsetShinyReactive(id = \"x_3\", value = reactiveExpression(\n  data.frame(x_1 = x_1, x_2 = x_2)), typed = TRUE)\n\nsetShinyReactive(id = \"x_4\", value = reactiveExpression(\n  list(\n    x_1 = summary(x_1), \n    x_2 = summary(x_2), \n    x_3_new = data.frame(x_3, prod = x_3$x_1 * x_3$x_2),\n    filenames = paste0(\"file_\", x_1)\n  )\n))\n```\n\nInspect:\n\n```\nx_1\n# [1] 1 2 3 4 5\n\nx_2\n# [1]  2  4  6  8 10\n\nx_3\n#   x_1 x_2\n# 1   1   2\n# 2   2   4\n# 3   3   6\n# 4   4   8\n# 5   5  10\n\nx_4\n# $x_1\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#       1       2       3       3       4       5 \n# \n# $x_2\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#       2       4       6       6       8      10 \n# \n# $x_3_new\n#   x_1 x_2 prod\n# 1   1   2    2\n# 2   2   4    8\n# 3   3   6   18\n# 4   4   8   32\n# 5   5  10   50\n# \n# $filenames\n# [1] \"file_1\" \"file_2\" \"file_3\" \"file_4\" \"file_5\"\n```\n\nChange values and inspect implications:\n\n```\n(x_1 \u003c- 100:102)\n# [1] 100 101 102\n\nx_2\n# [1] 200 202 204\n\nx_3\n#   x_1 x_2\n# 1 100 200\n# 2 101 202\n# 3 102 204\n\nx_4\n# $x_1\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#   100.0   100.5   101.0   101.0   101.5   102.0 \n# \n# $x_2\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#     200     201     202     202     203     204 \n# \n# $x_3_new\n#   x_1 x_2  prod\n# 1 100 200 20000\n# 2 101 202 20402\n# 3 102 204 20808\n# \n# $filenames\n# [1] \"file_100\" \"file_101\" \"file_102\"\n\n(x_1 \u003c- 1)\n# [1] 1\n\nx_2\n# [1] 2\n\nx_3\n#   x_1 x_2\n# 1   1   2\n\nx_4\n# $x_1\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#       1       1       1       1       1       1 \n# \n# $x_2\n#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. \n#       2       2       2       2       2       2 \n# \n# $x_3_new\n#   x_1 x_2 prod\n# 1   1   2    2\n# \n# $filenames\n# [1] \"file_1\"\n\ntry((x_1 \u003c- \"hello world!\"))\nx_1\n## --\u003e still `1:2` --\u003e overwrite has been ignored (due to `typed = TRUE`)\n```\n\nClean up:\n\n```\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\nrmReactive(\"x_3\")\nrmReactive(\"x_4\")\n```\n\n### Feature showcase 4: R6 and Reference Classes\n\nReference Classes:\n\n```\nTestRefClass \u003c- setRefClass(\"TestRefClass\", \n  fields = list(x_1 = \"numeric\", x_2 = \"numeric\"))\n\ninst \u003c- TestRefClass$new()\nclass(inst)\n\nsetShinyReactive(id = \"x_1\", value = 10, where = inst)\nsetShinyReactive(id = \"x_2\", \n  value = reactiveExpression(inst$x_1 * 2), where = inst)\ninst$x_1\ninst$x_2\n\n(inst$x_1 \u003c- 20)\ninst$x_2\n```\n\nR6 Classes:\n\n\n```\nTestR6 \u003c- R6Class(\"TestR6\", public = list(x_1 = \"numeric\", x_2 = \"numeric\"))\nsetOldClass(c(\"TestR6\", \"R6\"))\n\ninst \u003c- TestR6$new()\nclass(inst)\n\nsetShinyReactive(id = \"x_1\", value = 10, where = inst)\nsetShinyReactive(id = \"x_2\", \n  value = reactiveExpression(inst$x_1 * 2), where = inst)\ninst$x_1\ninst$x_2\n\n(inst$x_1 \u003c- 20)\ninst$x_2\n```\n\n### Feature showcase 4: setReactive() (legacy)\n\nNote that we set `verbose = TRUE` to enable the display of status messages that help understand what's going on.\n\nSet reactive object `x_1` that others can reference:\n\n```\nsetReactive(id = \"x_1\", value = 10, verbose = TRUE)\n```\n\nSet reactive object that references `x_1` and has a reactive binding of form `x_1 * 2` to it:\n\n```\nsetReactive(id = \"x_2\", value = function() {\n  \"object-ref: {id: x_1}\"\n  x_1 * 2\n}, verbose = TRUE)\n# Initializing ...\n\nx_1 \n# [1] 10\n\nx_2\n# [1] 20\n```\n\nWhenever `x_1` changes, `x_2` changes accordingly:\n\n\n```\n(x_1 \u003c- 100)\n# [1] 100\n\nx_2\n# Object: ab22808532ff42c87198461640612405\n# Called by: ab22808532ff42c87198461640612405\n# Modified reference: 2fc2e352f72008b90a112f096cd2d029\n#   - Checksum last: 2522027d230e3dfe02d8b6eba1fd73e1\n# \t- Checksum current: d344558826c683dbadec305ed64365f1\n# Updating ...\n# [1] 200\n```\n\nSee the examples of `setReactive()` for a short description of the information contained in the status messages\n\nNote that for subsequent requests and as long as `x_1` does not change, the value that has been cached during the last update cycle is used instead of re-running the binding function each time:\n\n```\nx_2\n# [1] 200\n## --\u003e cached value, no update\n\nx_2\n# [1] 200\n## --\u003e cached value, no update\n\n(x_1 \u003c- 1)\nx_2\n# Object: ab22808532ff42c87198461640612405\n# Called by: ab22808532ff42c87198461640612405\n# Modified reference: 2fc2e352f72008b90a112f096cd2d029\n#   - Checksum last: d344558826c683dbadec305ed64365f1\n# \t- Checksum current: 6717f2823d3202449301145073ab8719\n# Updating ...\n# [1] 2\n## --\u003e update according to binding function\n\nx_2\n# [1] 2\n## --\u003e cached value, no update\n```\n\nClean up \n\n```\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n\n-----\n\n## Unsetting reactive objects\n\nThis turns reactive objects (that are, even though hidden from the user, instances of class `ReactiveObject.S3`) into regular or non-reactive objects again. \n\n**Note that it does not mean the a reactive object is removed alltogether! See `rmReactive()` for that purpose**\n\n```\nsetReactive(id = \"x_1\", value = 10)\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\n\n## Illustrate reactiveness //\nx_1\nx_2\n(x_1 \u003c- 50)\nx_2\n\n## Unset reactive --\u003e turn it into a regular object again //\nunsetReactive(id = \"x_1\")\n```\nIllustration of removed reactiveness: \n\n```\nx_1\nx_2\n(x_1 \u003c- 10)\nx_2\n## --\u003e `x_1` is not a reactive object anymore; from now on, `x_2` simply returns\n## the last value that has been cached\n```\n\n### NOTE\nWhat happens when a reactive relationship is broken or removed depends on how you set argument `strictness_get` in the call to `setReactive()` or `setShinyReactive()`. \n\nAlso refer to vignette [Strictness](https://github.com/Rappster/reactr/blob/master/vignettes/strictness.Rmd) for more details.\n\n## Removing reactive objects\n\nThis deletes the object alltogether. \n\n```\nsetReactive(id = \"x_1\", value = 10)\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\n\n## Remove reactive --\u003e remove it from `where` //\nrmReactive(id = \"x_1\")\n\nexists(\"x_1\", inherits = FALSE)\n```\n\n-----\n\n# Reactivity scenarios in detail\n\nThe examples currently still use `setReactive()` instead of the recommended `setShinyReactive()` but should also work for `setShinyReactive()` given that you specify `value` via `reactiveExpression()` and that you do not want to use bi-directional bindings (as this is currently only possible when using `setReactive()`)\n\n## Scenario 1: one-directional (1)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` \n  \n- Binding/Relationship: \n\n  `A` uses value of `B` \"as is\", i.e. value of `A` identical to value of `B`\n\n### Example\n\nSet object `x_1` that others can reference:\n\n```\nsetReactive(id = \"x_1\", value = 10)\n```\n\nSet object that references `x_1` and has a reactive binding to it:\n\n```\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\n\nx_1 \nx_2\n\n```\n\nWhenever `x_1` changes, `x_2` changes accordingly:\n\n\n```\n(x_1 \u003c- 100)\n# [1] 100\n\nx_2\n# [1] 100\n\nx_2\n# [1] 100\n## --\u003e cached value as `x_1` has not changed; no update until `x_1` \n## changes again\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n-----\n\n## Scenario 2: one-directional (2)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` \n  \n- Binding/Relationship: \n\n  `A` transforms value of `B` , i.e. value of `A` is the result of applying a function on the value of `B`\n\n### Example\n\n```\nsetReactive(id = \"x_1\", value = 10)\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\nsetReactive(id = \"x_3\", value = function() {\n  \"object-ref: {id: x_1, as: ref_1}\"\n  ref_1 * 2\n})\n```\n\nNote how `x_3` changes according to its binding relationship `ref_1 * 2` (which is just a translation for `x_1 * 2`):\n\n```\nx_1 \n# [1] 10\n\nx_2\n# [1] 10\n\nx_3\n# [1] 20\n## --\u003e x_1 * 2\n\n(x_1 \u003c- 500)\nx_2\n# [1] 500\n\nx_3\n# [1] 1000\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\nrmReactive(\"x_3\")\n```\n\n-----\n\n## Scenario 3: one-directional (3)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` and `C`, `B` references `C`\n  \n- Binding/Relationship: \n\n  `A` transforms value of `B` , i.e. value of `A` is the result of applying a function on the value of `B`\n\n### Example\n\n```\nsetReactive(id = \"x_1\", value = 10)\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\nsetReactive(id = \"x_3\", value = function() {\n  \"object-ref: {id: x_1, as: ref_1}\"\n  \"object-ref: {id: x_2, as: ref_2}\"\n  ref_1 + ref_2 * 2\n})\n```\n\nNote how each object that is involved changes according to its binding relationships:\n\n```\nx_3\n# [1] 30\n\n(x_1 \u003c- 100)\n\nx_3\n[1] 300\n\n(x_2 \u003c- 1)\nx_2\n## --\u003e disregarded as `x_2` has a one-directional binding to `x_1`, hence does \n## not accept explicit assignment values\n\nx_3\n# [1] 300\n\n(x_1 \u003c- 50)\nx_2\n# [1] 50\n\nx_3\n# [1] 150\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\nrmReactive(\"x_3\")\n```\n\n## Scenario 4: bi-directional (1)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` and `B` references `A` --\u003e bidirectional binding type\n  \n- Binding/Relationship: \n\n  `A` uses value of `B` \"as is\" and `B` uses value of `A` \"as is\". This results in a **steady state**. \n\n### Example\n\nA cool feature of this binding type is that you are free to alter the values of *both* objects and still keep everything \"in sync\"\n\n```\nsetReactive(id = \"x_1\", function() \"object-ref: {id: x_2}\")\nsetReactive(id = \"x_2\", function() \"object-ref: {id: x_1}\")\n```\n\nNote that the call to `setReactive()` merely initializes objects with bidirectional bindings to the value `numeric(0)`:\n\n```\nx_1\n# NULL\n\nx_2\n# NULL\n```\n\nYou must actually assign a value to either one of them via `\u003c-` **after** establishing the binding:\n\n```\n## Set actual initial value to either one of the objects //\n(x_1 \u003c- 100)\n# [1] 100\n\nx_2\n# [1] 100\n\nx_1\n# [1] 100\n\n## Changing the other one of the two objects //\n(x_2 \u003c- 1000)\n# [1] 1000\n\nx_1\n# [1] 1000\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n\n## Scenario 5: bi-directional (2)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` and `B` references `A` --\u003e bidirectional binding type\n  \n- Binding/Relationship: \n\n  `A` uses transformed value of `B` and `B` uses transformed value of `A`. \n  \n  The binding functions used result in a **steady state**.\n\n### Example\n\nAs the binding functions are \"inversions\"\" of each other, we still get to a steady state.\n\n```\nsetReactive(id = \"x_1\", function() {\n  \"object-ref: {id: x_2}\"\n  x_2 * 2\n})\n\nsetReactive(id = \"x_2\", function() {\n  \"object-ref: {id: x_1}\"\n  x_1 / 2\n})\n```\n\nNote that due to the structure of the binding functions, the visible object values are initialized to `numeric()` instead of `NULL` now.\n\n```\nx_1\n# numeric(0)\n\nx_2\n# numeric(0)\n```\n\nHere, we always reach a steady state, i.e. a state in which cached values can be used instead of the need to executed the binding functions.\n\n```\n## Set actual initial value to either one of the objects //\n(x_1 \u003c- 100)\n# [1] 100\n\nx_2\n# [1] 50\n\nx_1\n# [1] 100\n\n## Changing the other one of the two objects //\n(x_2 \u003c- 1000)\n# [1] 1000\n\nx_1\n# [1] 2000\n\nx_2\n# [1] 1000\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n\n## Scenario 6: bi-directional (3)\n\n### Scenario explanation\n\n- Type/Direction: \n\n  `A` references `B` and `B` references `A` --\u003e bidirectional binding type\n  \n- Binding/Relationship: \n\n  `A` uses transformed value of `B` and `B` uses transformed value of `A`. \n  \n  The binding functions used result in a **non-steady state**.\n\n### Example\n\nAs the binding functions are **not** \"inversions\"\" of each other, we never reach/stay at a steady state. Cached values are/can never be used as by the definition of the binding functions the two objects are constantly updating each other.\n\n```\nsetReactive(id = \"x_1\", function() {\n  \"object-ref: {id: x_2}\"\n  x_2 * 2\n})\n\nsetReactive(id = \"x_2\", function() {\n  \"object-ref: {id: x_1}\"\n  x_1 * 10\n})\n```\n\nHere, we have \"non-steady-state\" behavior, i.e. we never reach a state were cached values can be used. We always need to execute the binding functions as each request of a visible object value results in changes. \n\nThis is best verified when using `verbose = TRUE` and comparing it to the other scenarios (not done at this point).\n\n```\nx_1\n# numeric(0)\n\nx_2\n# numeric(0)\n\n## Set actual initial value to either one of the objects //\n(x_1 \u003c- 1)\n# [1] 1\n\nx_2\n# [1] 10\n## --\u003e `x_1` * 10\n\nx_1\n# [1] 20\n## --\u003e x_2 * 2\n\nx_2\n# [1] 200\n## --\u003e `x_1` * 10\n\n## Changing the other one of the two objects //\n(x_2 \u003c- 1)\n# [1] 1\n\nx_1\n# [1] 2\n\nx_2\n# [1] 20\n\nx_1\n# [1] 40\n\n## Clean up //\nrmReactive(\"x_1\")\nrmReactive(\"x_2\")\n```\n\n----\n\n## Caching mechanism (overview)\n\n### NOTE\n\nThis is only necessary/usefull when using `setReactive()` as `setShinyReactive()` as shiny takes care of registering and caching itself.\n\nThe package implements a caching mechanism that (hopefully) contributes to an efficient implementation of reactivity in R in the respect that binding functions are only executed when they actually need to.\n\nAs mentioned above, this *might* be unnecessary or even counter-productive in situations where the runtime of binding functions is negligible, but help in situations where unnecessary executions of binding functions is not desired due to their specific nature or long runtimes.\n\nA second reason why the caching mechanism was implemented is to offer the possibility to specify *bi-directional* reactive bindings. AFAICT, you need some sort of caching mechanism in order to avoid infinite recursions.\n\nSee vignette [Caching](https://github.com/Rappster/reactr/blob/master/vignettes/caching.Rmd) for details on this.\n\n### The registry\n\nCaching is implemented by storing references of the \"hidden parts\" of an reactive object (the hidden instances of class `ReactiveObject.S3`) in a registry that is an `environment` and lives in `getOption(\"reactr\")$.registry`.\n\n### Convenience functions\n\nEnsuring example content in registry:\n\n```\nresetRegistry()\nsetReactive(id = \"x_1\", value = 10)\nsetReactive(id = \"x_2\", value = function() \"object-ref: {id: x_1}\")\n```\n\n#### Get the registry object\n\n```\nregistry \u003c- getRegistry()\n```\n\n#### Show registry content\n\n```\nshowRegistry()\n```\n\nThe registry contains the UIDs of the reactive objects that have been set via `setReactive`. See `computeObjectUid()` for the details of the computation of object UIDs.\n\n#### Retrieve from registry\n\n```\nx_1_hidden \u003c- getFromRegistry(id = \"x_1\")\nx_2_hidden \u003c- getFromRegistry(id = \"x_2\")\n\n## Via UID //\ngetFromRegistry(computeObjectUid(\"x_1\"))\ngetFromRegistry(computeObjectUid(\"x_2\"))\n\n```\n\nThis object corresponds to the otherwise \"hidden part\"\" of `x_1` that was implicitly created by the call to `setReactive()`.\n\n```\nclass(x_1_hidden)\nls(x_1_hidden)\n\n## Some interesting fields //\nx_1_hidden$.id\nx_1_hidden$.where\nx_1_hidden$.uid\nx_1_hidden$.value\nx_1_hidden$.hasPullReferences()\n\nx_2_hidden$.id\nx_2_hidden$.where\nx_2_hidden$.uid\nx_2_hidden$.value\nx_2_hidden$.has_cached\nx_2_hidden$.hasPullReferences()\nls(x_2_hidden$.refs_pull)\nx_2_hidden$.refs_pull[[x_1_hidden$.uid]]\n```\n#### Remove from registry\n\n```\n## Via ID (and `where`) //\nrmFromRegistry(id = \"x_1\")\n## --\u003e notice that entry `2fc2e352f72008b90a112f096cd2d029` has been removed\n\n## Via UID //\nrmFromRegistry(computeObjectUid(\"x_2\"))\n## --\u003e notice that entry `ab22808532ff42c87198461640612405` has been removed\n```\n\n#### Reset registry\n\n```\nshowRegistry()\nresetRegistry()\nshowRegistry()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjankowtf%2Freactr","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjankowtf%2Freactr","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjankowtf%2Freactr/lists"}