{"id":16958066,"url":"https://github.com/joshnuss/glue","last_synced_at":"2025-09-09T13:36:30.459Z","repository":{"id":47394867,"uuid":"202460109","full_name":"joshnuss/glue","owner":"joshnuss","description":"React hook for server-side state","archived":false,"fork":false,"pushed_at":"2023-01-04T07:46:50.000Z","size":1452,"stargazers_count":17,"open_issues_count":14,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-22T11:51:02.177Z","etag":null,"topics":["elixir","erlang","phoenix","react","websocket"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/joshnuss.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}},"created_at":"2019-08-15T02:31:32.000Z","updated_at":"2024-07-25T12:18:03.000Z","dependencies_parsed_at":"2023-02-02T04:02:02.512Z","dependency_job_id":null,"html_url":"https://github.com/joshnuss/glue","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/joshnuss/glue","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshnuss%2Fglue","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshnuss%2Fglue/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshnuss%2Fglue/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshnuss%2Fglue/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/joshnuss","download_url":"https://codeload.github.com/joshnuss/glue/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/joshnuss%2Fglue/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274302612,"owners_count":25260249,"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-09-09T02:00:10.223Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","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":["elixir","erlang","phoenix","react","websocket"],"created_at":"2024-10-13T22:21:19.922Z","updated_at":"2025-09-09T13:36:30.429Z","avatar_url":"https://github.com/joshnuss.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Easy server-side state with React \u0026 Phoenix\n\nReact is great at rendering HTML, but sharing state can be tricky.\n\nOne technology that is designed for managing large amounts of shared state is the [Actor Model](https://en.wikipedia.org/wiki/Actor_model), known in Erlang as `GenServer`. \n\nBy relying on Erlang for state management, it frees us from needing to write boilerplate Redux, hooks and/or GraphQL/REST wrappers and just write GenServer code. The React hooks and Websocket code is then generated for you.\n\n[View live demo](http://react-use-server-state.gigalixirapp.com/)\n\n## Counter Example\n\nLet's take the example of a simple counter. This is what it looks like with React's `useState()` hook:\n\n```js\nimport React, {useState} from 'react'\n\nexport default function() {\n  const [count, set] = useState(0)\n\n  return (\n    \u003cdiv\u003e\n      \u003cbutton onClick={() =\u003e set(count + 1)}\u003e-\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e set(count - 1)}\u003e+\u003c/button\u003e\n\n      \u003ch2\u003e{count}\u003c/h2\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nInstead of storing state in the local browser's memory with `useState()`, we can switch to server-side state by simply swapping `useState()` to `useServerState()`: \n\n```js\nimport React from 'react'\nimport {useServerState} from '@glue/react'\n\nexport default function() {\n  const {count, increment, decrement} = useServerState('counter', {sync: true})\n  \n  return (\n    \u003cdiv className=\"counter\"\u003e\n      \u003cbutton onClick={() =\u003e decrement()}\u003e-\u003c/button\u003e\n      \u003cbutton onClick={() =\u003e increment()}\u003e+\u003c/button\u003e\n\n      \u003ch2\u003e{count}\u003c/h2\u003e\n    \u003c/div\u003e\n  )\n}\n```\n\nThe state will first be queried from the server. Subsequent changes will be synced back to the server and then between all connected users via Websockets.\n\nNow we just need to define a `GenServer` to hold the state in the backend:\n\n```elixir\ndefmodule Counter do\n  use Agent\n\n  @name :counter\n\n  def start_link(_opts),\n    do: Agent.start_link(fn -\u003e 0 end, name: @name)\n\n  def get(), do: Agent.get(@name, \u0026 \u00261)\n  def increment(), do: apply(+1)\n  def decrement(), do: apply(-1)\n\n  defp apply(delta) do\n    Agent.get_and_update(@name, \u0026{\u00261 + delta, \u00261 + delta})\n  end\nend\n```\n\nThat's it! Just `GenServer`'s and pure JSX components.\n\n![Counter example](/counter-example.gif)\n\n### This is alpha software.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshnuss%2Fglue","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjoshnuss%2Fglue","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjoshnuss%2Fglue/lists"}