{"id":15487029,"url":"https://github.com/jogboms/declarative_reactivity_workshop","last_synced_at":"2025-07-23T22:36:51.691Z","repository":{"id":207364457,"uuid":"717370757","full_name":"jogboms/declarative_reactivity_workshop","owner":"jogboms","description":"Implement a declarative reactivity system from scratch.","archived":false,"fork":false,"pushed_at":"2024-06-07T15:05:33.000Z","size":204,"stargazers_count":22,"open_issues_count":0,"forks_count":4,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-22T15:49:07.640Z","etag":null,"topics":["dartlang","declarative-programming","flutter","functional-reactive-programming","reactive-programming","reactivity-system","state-management","transparent-functional-reactive-programming"],"latest_commit_sha":null,"homepage":"https://dartpad.dev/?id=5aa877b61eb9fccd904c325f2757a05a","language":"Dart","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/jogboms.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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}},"created_at":"2023-11-11T09:19:19.000Z","updated_at":"2024-06-07T15:05:40.000Z","dependencies_parsed_at":"2024-06-07T16:31:15.404Z","dependency_job_id":"3d7a478e-d0cc-4465-a9de-ce2d28c3dde6","html_url":"https://github.com/jogboms/declarative_reactivity_workshop","commit_stats":null,"previous_names":["jogboms/declarative_reactivity_workshop"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jogboms/declarative_reactivity_workshop","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jogboms%2Fdeclarative_reactivity_workshop","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jogboms%2Fdeclarative_reactivity_workshop/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jogboms%2Fdeclarative_reactivity_workshop/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jogboms%2Fdeclarative_reactivity_workshop/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jogboms","download_url":"https://codeload.github.com/jogboms/declarative_reactivity_workshop/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jogboms%2Fdeclarative_reactivity_workshop/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266761410,"owners_count":23980296,"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","status":"online","status_checked_at":"2025-07-23T02:00:09.312Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["dartlang","declarative-programming","flutter","functional-reactive-programming","reactive-programming","reactivity-system","state-management","transparent-functional-reactive-programming"],"created_at":"2024-10-02T06:20:52.218Z","updated_at":"2025-07-23T22:36:51.633Z","avatar_url":"https://github.com/jogboms.png","language":"Dart","readme":"# Declarative Reactivity Workshop\n\nThis workshop will help you understand how to implement a declarative reactivity system similar\nto [Riverpod](https://riverpod.dev/docs/introduction/getting_started).\n\nThis is a common pattern in the web development space with examples\nlike [MobX](https://mobx.js.org/the-gist-of-mobx.html), [Solid-js's Signals](https://www.solidjs.com/guides/reactivity), [Vue's Composition\nAPI](https://vuejs.org/guide/introduction.html), [React's Recoil-js](https://recoiljs.org/docs/introduction/core-concepts)\nand more recently [Svelte's Runes](https://svelte.dev/blog/runes).\n\nThis is a very simplified version of a reactivity system with a couple of missing features. Most-notable a robust \nerror handling system. But it will help you understand how a basic reactivity system works internally.\n\nThis workshop is also not meant to teach you how to use Riverpod, it is actually only mentioned as a more robust\nimplementation with a familiar API but rather how to implement a similar system from scratch without all the bells and\nwhistles.\n\n## What is reactivity?\n\nReactivity is a programming paradigm that focuses on how data flows through an application. A reactive system will\nautomatically update when data changes.\n\nThis is in contrast to the imperative style, which requires the developer to manually update the UI when data changes.\n\nIn the case of being declarative, you are tasked with describing what you want the data to look like when a change\noccurs and the details of when this happens and how it happens is handled automatically by the framework.\n\n## Why is declarative reactivity important?\n\nDeclarative reactivity is important because it allows us to focus on what we want our system to look like, rather\nthan how we want our system to behave. This allows us to write code that is easier to understand, test, and maintain.\n\n## What makes a declarative reactive system?\n\n### Declarative\n\nWe should only be able to mutate only our state and not the state of other objects.\n\n### Reactive\n\nWe should be able to subscribe to changes in other reactive objects and be notified when they\noccur.\n\n### Composable\n\nWe should be able to combine multiple reactive objects into a single reactive object.\n\n### Performant\n\nWe should be able to update our state without causing unnecessary re-renders.\n\n### Predictable\n\nWe should be able to predict how our state will change when we mutate it.\n\n### Testable\n\nWe should be able to test our state without having to mock out the entire system.\n\n## How does it actually work?\n\nIts important again to understand that declarative reactivity is not a new concept. It has been around for a long\ntime in the form of the Observer Pattern. The Observer Pattern is a design pattern that allows objects to subscribe\nto changes in other objects. This is useful when you want to be notified of changes in an object, but don't want to\nhave to poll for changes.\n\nThe Observer Pattern is a good starting point for understanding how declarative reactivity works, but it has some\nlimitations in its raw form especially when it comes to self-clean up and potential memory leaks. We would be\naddressing some of these in our implementation.\n\n## What does it look like in code?\n\n### Riverpod\n\n```dart\n\nfinal countProvider = StateProvider((ref) =\u003e 0);\n\nclass Counter extends HookWidget {\n  @override\n  Widget build(BuildContext context) {\n    final count = useProvider(countProvider).state;\n\n    useEffect(() {\n      final timer = Timer.periodic(Duration(seconds: 1), (_) {\n        context\n            .read(countProvider)\n            .state++;\n      });\n\n      return timer.cancel;\n    }, []);\n\n    return Text('Count: $count');\n  }\n}\n```\n\n### Solid-js\n\n```jsx\nimport { createSignal, createEffect } from \"solid-js\";\n\nfunction Counter() {\n  const [count, setCount] = createSignal(0);\n\n  setInterval(() =\u003e setCount(count() + 1), 1000);\n  \n  createEffect(() =\u003e console.log(count()));\n\n  return \u003cp\u003eCount: {count()}\u003c/p\u003e;\n}\n```\n\n### Vue\n\n```vue\n\u003cscript setup\u003e\nimport { ref, watch } from 'vue'\n\nconst count = ref(0)\n\nsetInterval(() =\u003e count.value++, 1000)\n\nwatch(count, () =\u003e console.log(count.value))\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cp\u003eCount: {{ count }}\u003c/p\u003e\n\u003c/template\u003e\n```\n\n### Svelte\n\n```svelte\n\u003cscript\u003e\n   let count = $state(0);\n\n   setInterval(() =\u003e count += 1, 1000);\n  \n   $effect(() =\u003e console.log(count));\n\u003c/script\u003e\n\n\u003cp\u003eCount: {count}\u003c/p\u003e\n```\n\n## What we will be building\n\n```dart\n\nfinal count = Atom((_) =\u003e 0);\n\nvoid main() {\n  final container = AtomContainer();\n\n  Timer.periodic(const Duration(seconds: 1), (_) {\n    container.mutate(count, (value) =\u003e value + 1);\n  });\n\n  container.listen(count, (_, value) {\n    print(value);\n  });\n}\n\n```\n\n## Lets Begin!\n\nYou can follow along by checking out the commits.\n\nA live flutter demo can also be accessed on [Dartpad](https://dartpad.dev/?id=5aa877b61eb9fccd904c325f2757a05a). \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjogboms%2Fdeclarative_reactivity_workshop","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjogboms%2Fdeclarative_reactivity_workshop","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjogboms%2Fdeclarative_reactivity_workshop/lists"}