{"id":26422344,"url":"https://github.com/jollytoad/loflux-js","last_synced_at":"2025-10-25T23:40:53.166Z","repository":{"id":27119040,"uuid":"30587188","full_name":"jollytoad/loflux-js","owner":"jollytoad","description":"Obsolete (replaced by Fluxlet)","archived":false,"fork":false,"pushed_at":"2015-10-06T18:55:07.000Z","size":312,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-17T00:28:58.276Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":false,"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/jollytoad.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":"2015-02-10T10:32:33.000Z","updated_at":"2015-10-06T18:56:54.000Z","dependencies_parsed_at":"2022-09-01T00:00:28.718Z","dependency_job_id":null,"html_url":"https://github.com/jollytoad/loflux-js","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Floflux-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Floflux-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Floflux-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jollytoad%2Floflux-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jollytoad","download_url":"https://codeload.github.com/jollytoad/loflux-js/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244141434,"owners_count":20404836,"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":"2025-03-18T02:03:54.886Z","updated_at":"2025-10-25T23:40:53.076Z","avatar_url":"https://github.com/jollytoad.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"**NOTE: This project has been superseded by [Fluxlet](https://github.com/fluxlet/fluxlet)**\n\n## LoFlux is not another framework\n\nIt's not even a library (yet).\n\nIt a simple idea, inspired by the uni-directional data flow concept from Flux, and the idea that presentation is a pure\nfunction of the application state.\n\nFirstly don't get me wrong, I like the idea of Flux and React, but they both feel overly complex to me, and it doesn't\nseem clear where asynchronous requests fit in.\n\nSo I started thinking about data flow, representation of state, asynchronous requests and side effects.\n\nI started to think of it as a game.\n\n## The rules\n\nThere is a state of play, a player can manipulate that state of player, and a spectator/commentator observes that state\nand can report on it.\n\nThe player is a pure function, which is given a cue (eg. from an event) and the current state of play, and returns a\nnew state of play.\n\n(The player may in fact be a whole chain of plays, passing a new state to one another)\n\nIf the state has changed the spectator is passed the modified state from the player, and may perform any necessary\nside-effects, but it MUST NOT modify the state.\n\nThe side-effects may be rendering to the DOM, or performing an async request, calling a web-worker, or setting a timeout\nto perform another *action*.\n\nThe whole *action* described above occurs purely within a distinct period of the event loop (TODO: is there a name for this?),\nas the following diagram shows:\n\n![LoFlux data flow](loflux-data-flow.png)\n\nTime runs from left to right, rectangles are code, green lines are the payload/cue/stimulus from events, red lines are\nthe flow of state data.\n\n## The action\n\nYou may have noticed the term *action* used above, an *action* is a function that:\n\n* was created with a player, and spectator\n* is called with a cue (some parameters for the player)\n* fetches the current state\n* calls the player with the cue and state\n* checks the whether the new state returned from the player differs to the original state, and if so...\n    * stores the new state\n    * calls the spectator with the new and old state\n\nThe player is a pure referentially transparent function, it uses data only from its arguments, ie. the cue and state,\nand returns either the same state or a new state (state is immutable btw). It must NEVER perform side-effects;\nno DOM manipulation, no async requests, no timeouts, no mutation of any external data, and must not call another *action*.\n\nThe spectator is passed the new state and old state, but not the cue. It can, in contrast to the player, perform any\ntype of side-effect it wishes, but must not modify the state given to it or attempt to directly call another *action*.\nAlthough it may set a timeout to call another *action*.\n\nThis leaves us with the question of where we store state between *actions*...\n\n## The locker\n\nThis is an object that holds an immutable state, it is passed into the *action* which will *claim* the state, *swap* in\nthe new state, and *release* it after all side-effects have executed.\n\nWhen the state is *claimed* from the *locker* it is locked, so no other *action* can claim it - an error is thrown if\nattempted. This prevents *actions* from being nested, ie. called by players or spectators.\n\nThe *release* at the end of the *action* will unlock it, allowing another *action* to execute.\n\n## The round\n\nPassing the locker and cue, and player, and spectator into every *action* would be quite tedious. So instead we have\nthe *round*, which will help us create an *action factory* to create *actions*.\n\nA *round* is a function that:\n\n* is created from a locker, spectator and player\n* accepts the cue as its parameters\n* claims the current state from the locker\n* calls a player with the cue and state\n* checks the whether the new state returned from player differs to the original state, and if so...\n    * swaps the new state into the locker\n    * calls a spectator with the new state and old state\n* releases the locker\n\nThe *round* constructor function can be partially applied given a locker and spectator to create an *action factory*.\nThis provides a very convenient way to create new actions that pull the same state and perform the same side-effects\nbut have a unique player (ie. state manipulation).\n\n## The name\n\nLoFlux is a lower level flux. (Suggestions for a better name are very welcome)\n\n## The code\n\nI'd recommend perusing the code in the following order:\n\n* [the locker](loflux/src/locker.ts) and [tests](loflux/test/locker.ts)\n* [the round](loflux/src/round.ts) and [tests](loflux/test/round.ts)\n* [example of how an action factory can be created](examples/react/src/init.ts)\n* [example of how actions are created using the factory](examples/react/src/actions.ts) and [tests](examples/react/test/actions.ts)\n* [the utility to update an immutable state](utils/src/update.ts)\n* [example of how actions are called from event handlers](examples/react/src/app.jsx) and [this](examples/react/src/todoItem.jsx)\n\n## The design\n\nYou may have noticed, there is very little in the way of OO design here - this is entirely intentional.\n\nI've intended this to be a low level idea built around functional concepts. I'd expect anyone using LoFlux to\nbuild their own *HiFlux* (ie. Higher level flux) on top, using whatever libs and programming paradigm you prefer.\n\n## The utilities\n\nYou may have noticed the utils directory, and the usage of these throughout the examples. These are simple utility\nfunctions, provided here to avoid depending on any particular library - although I'd recommend using your preferred\nutility lib instead, eg Underscore, Lodash, Ramda, etc rather than these in practice.\n\n## The future\n\nI hope to add:\n\n* documentation within the code\n* examples using alternative libraries\n* demonstrations of different side-effects\n* ways to delegate to and register side-effects\n* a better build\n* ES6 modules when Typescript 1.5 is released\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjollytoad%2Floflux-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjollytoad%2Floflux-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjollytoad%2Floflux-js/lists"}