{"id":27935810,"url":"https://github.com/rt2zz/redux-remotes","last_synced_at":"2025-05-07T06:50:46.352Z","repository":{"id":35758536,"uuid":"40037887","full_name":"rt2zz/redux-remotes","owner":"rt2zz","description":"Remote Interactions","archived":false,"fork":false,"pushed_at":"2015-10-03T06:00:13.000Z","size":224,"stargazers_count":14,"open_issues_count":0,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-05T02:35:25.462Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"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/rt2zz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-08-01T06:33:47.000Z","updated_at":"2024-02-23T19:57:16.000Z","dependencies_parsed_at":"2022-08-31T06:21:55.779Z","dependency_job_id":null,"html_url":"https://github.com/rt2zz/redux-remotes","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rt2zz%2Fredux-remotes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rt2zz%2Fredux-remotes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rt2zz%2Fredux-remotes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rt2zz%2Fredux-remotes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rt2zz","download_url":"https://codeload.github.com/rt2zz/redux-remotes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252453974,"owners_count":21750387,"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-05-07T06:50:45.764Z","updated_at":"2025-05-07T06:50:46.345Z","avatar_url":"https://github.com/rt2zz.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Redux Remotes\nTrigger side-effects (e.g. async actions) via dispatch. Vaguely similar to cerebral signals or elm side effects.\n\nRemotes provides a standard, predicatable API for handling async actions. It is similar to using redux-thunk, except instead of dispatching a function, you dispatch a \"command\" action which is then handled by one or many remotes. There are potentially a few benefits to this approach:\n* Serializable (as opposed to action creator invocation which is not)\n* Robust and standardized logging\n* Many side effects can be triggered by one action, or one side effect can be triggered by multiple actions\n\nAdditionally because the structure of remotes mirrors that of reducers, the mental model is light and easy to integrate within an existing redux application.\n\nNot necessarily redux specific, but that is the target architecture.\n\n## What does it look like?\nRemotes works as follows:  \n1. Compose multiple `remotes` into a single `remote` function (just like you do with reducers  \n2. Install the middleware. The middleware sends every action to the registered remote before passing it along.   \n3. A contract is created for every action that one more remotes handles.  \n4. Each remote calls finish() when it is done operating on an action.  \n\nTo get a better idea of what this looks like, see the console logging upon contract completion:\n\u003chr /\u003e\n\u003cimg style=\"border:2px solid #aaaaaa;\" src=\"https://raw.githubusercontent.com/rt2zz/redux-remotes/master/examples/log.png\" /\u003e\n\n## Usage\n```js\nimport { createRemote, remotesMiddleware } from 'redux-remotes'\nimport * as remotes from '../remotes'\n\nconst remote = createRemote(remotes, {log: true})\nconst remoteMW = remotesMiddleware(remote)\nconst createStoreWithMiddleware = applyMiddleware(remoteMW)(createStore)\n```\nin reducers/someReducer.js\n```js\nimport { INCREMENT } from '../constants/ActionTypes'\n\nexport default function account({action, getState, finish, dispatch}) {\n  switch (action.type) {\n\n  case INCREMENT:\n    //call finish when done operating so the contract can be closed.\n    setTimeout(finish, 1000)\n    //return true indicates this remote is going to operate, and the contract should wait for response\n    return true\n\n  default:\n    //return false if no operation\n    return false\n  }\n}\n```\n\n## Use Cases\nRestful Resource\n```js\nexport default function profile({action, getState, finish, dispatch}) {\n  switch (action.type) {\n\n  case PROFILE_CREATE:\n    let profile = {...action.data, timestamp: Date.now()}\n    profilePending(profile)\n    apiClient.createProfile(profile, (err) =\u003e {\n      if(err){ profileFail(profile) }\n      else{ profileSuccess(profile) }\n      finish()\n    })\n    return true\n\n  default:\n    return false\n  }\n\n  function profilePending(profile){\n    dispatch({ type: PROFILE_CREATE_PENDING, profile: profile})\n  }\n  function profileFail(profile){\n    dispatch({ type: PROFILE_CREATE_FAIL, profile: profile})\n  }\n  function profileSuccess(profile){\n    dispatch({ type: PROFILE_CREATE_SUCCESS, profile: profile })\n  }\n}\n```\n\nOther times remotes may not need to report their status as actions. For example a remote action logger:\n```js\nexport default function remoteLogger({action, getState, finish, dispatch}) {\n  remoteLog(action, () =\u003e {\n    finish()\n  })\n  return true\n}\n```\n\nOr a remote stream\n```js\nexport default function alertPipe({action, getState, finish, dispatch}) {\n  switch (action.type) {\n\n  let unsubscribe = null\n\n  case SUBSCRIBE_TO_ALERTS:\n    listener = rethinkDBListener((alert) =\u003e {\n      dispatch({\n        type: 'ALERT',\n        alert: alert,\n      })\n    })\n    unsubscribe = () =\u003e {\n      listener.destroy()\n      finish()\n    }\n    //finish is never called, meaning this will always show\n    return true\n\n  case UNSUBSCRIBE_TO_ALERTS:\n    unsubscribe \u0026\u0026 unsubscribe()\n    finish()\n    return true\n\n  default:\n    return false\n}\n```\n\n## Uncertainties\nThis may need some tweaking to play well with store enhancers like redux-devtools. Further testing and experimentation is needed.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frt2zz%2Fredux-remotes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frt2zz%2Fredux-remotes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frt2zz%2Fredux-remotes/lists"}