{"id":18465177,"url":"https://github.com/sleexyz/efx","last_synced_at":"2025-10-25T22:13:32.696Z","repository":{"id":57220242,"uuid":"117724197","full_name":"sleexyz/efx","owner":"sleexyz","description":"Effectful state mangement for React, designed for simplicity, testability and type-safety","archived":false,"fork":false,"pushed_at":"2018-05-12T19:07:50.000Z","size":55,"stargazers_count":5,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-23T09:35:32.369Z","etag":null,"topics":["flow","flux","react","redux","state-management"],"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/sleexyz.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":"2018-01-16T18:31:09.000Z","updated_at":"2023-10-02T12:24:33.000Z","dependencies_parsed_at":"2022-08-29T00:11:57.996Z","dependency_job_id":null,"html_url":"https://github.com/sleexyz/efx","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/sleexyz%2Fefx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sleexyz%2Fefx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sleexyz%2Fefx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sleexyz%2Fefx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sleexyz","download_url":"https://codeload.github.com/sleexyz/efx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247804534,"owners_count":20999007,"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":["flow","flux","react","redux","state-management"],"created_at":"2024-11-06T09:12:15.030Z","updated_at":"2025-10-25T22:13:32.611Z","avatar_url":"https://github.com/sleexyz.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EFX\n\n[![CircleCI](https://circleci.com/gh/sleexyz/efx.svg?style=svg)](https://circleci.com/gh/sleexyz/efx)\n[![npm version](https://img.shields.io/npm/v/efx.svg?style=flat-square)](https://www.npmjs.com/package/efx)\n\n\nAn effectful state management system for React applications, designed for simplicity, testability and type-safety.\n\nInspired by redux and reader monads.\n\n**Table of Contents**\n\n* [Installation](#installation)\n* [Centralized State](#centralized-state)\n* [Actions](#actions)\n* [Connecting React Components to the Store](#connecting-react-components-to-the-store)\n* [Testing](#testing)\n* [Why not Redux?](#why-not-redux)\n\n\n## Installation\n\n```\nyarn add efx\n```\n\n## Centralized State\n\nTo achieve full type coverage, EFX requires a single file of boilerplate, conventionally named `efx.js`. This file can be more or less copy-pasted with the following contents:\n\n\n```js\n// ./efx.js\n\n// @flow\nimport * as EFX from \"efx\";\n\nexport type AppState = {\n  foo: number\n};\n\nexport const Store: Class\u003cEFX.Store\u003cAppState\u003e\u003e = EFX.Store;\n\nexport const Provider: Class\u003cEFX.Provider\u003cAppState\u003e\u003e = EFX.Provider;\n\nexport type Action\u003cA, B\u003e = A =\u003e Store =\u003e B;\n\nexport const makeAction: \u003cA, B\u003e(Action\u003cA, B\u003e) =\u003e Action\u003cA, B\u003e = EFX.makeAction;\n\ntype DefaultProps = {};\n\nexport type Connect\u003cOP: {}, CP: {}\u003e = EFX.Connect\u003cOP, CP, AppState, DefaultProps\u003e;\n\nexport function connect\u003cCP: {}, OP: {}\u003e(\n  component: React.ComponentType\u003cConnect\u003cOP, CP\u003e\u003e,\n  mapStateToProps: AppState =\u003e CP\n): React.ComponentType\u003cOP\u003e {\n  const defaultProps = {};\n  return EFX.connect(component, mapStateToProps, defaultProps);\n}\n```\n\nYou can find an example `./efx.js` in production [here](https://github.com/sleexyz/gallium/blob/master/gallium-live/src/efx.js).\n\n## Actions\n\nAn Action is an effectful function.\n\nIt is the single unit of abstraction in EFX; no reducers, no action creators, no constants.\n\n### Synchronous actions:\n\nTo create an action that modifies some state:\n\n```js\n// ./AppActions.js\n\n// @flow\nimport { type Action, makeAction } from \"./efx\";\n\nexport const addToFoo: Action\u003cnumber, number\u003e = makeAction(number =\u003e store =\u003e {\n  store.state.foo += 1;\n  return store.state.foo;\n});\n```\n\nTo dispatch an action, e.g. in the body of some other action:\n\n```js\nexport const addTenToFooAndLog: Action\u003cvoid, void\u003e = makeAction(() =\u003e store =\u003e {\n  const returnValue = store.dispatch(addToFoo(10));\n  console.log(\"value of foo: \" + returnValue);\n});\n```\n\n### Asynchronous actions:\n\nTo create an **async** action that modifies some state:\n\n```js\n// ./AppActions.js\n\n// @flow\nimport { type Action, makeAction } from \"./efx\";\n\nexport const addToFooAsync: Action\u003cnumber, Promise\u003cnumber\u003e\u003e = makeAction(number =\u003e async context =\u003e {\n\n  await new Promise(resolve =\u003e setTimeout(resolve, 1000));\n\n  context.state.foo += 1;\n  return context.state.foo;\n});\n```\n\nTo dispatch an async action, e.g. in the body of some other async action:\n\n```js\nexport const addTenToFooAndLogAsync: Action\u003cvoid, Promise\u003cvoid\u003e\u003e = makeAction(() =\u003e async store =\u003e {\n  const returnValue = await store.dispatch(addToFoo(10));\n  console.log(\"value of foo: \" + returnValue);\n});\n```\n\n##  Connecting React Components to the Store\n\nThe React component below does the following:\n\n1. Renders text derived from the value of `store.state.foo`\n2. On click of a button, modifies the value of `store.state.foo`\n\n```js\n// ./MyComponent.js\n\n// @flow\nimport * as React from \"react\";\nimport { connect, type Connect } from \"./efx\";\n\ntype OwnProps = {};\n\ntype ContainerProps = {\n  foo: number\n};\n\nconst mapStateToProps = ({ foo }) =\u003e ({ foo });\n\nclass _MyComponent extends React.Component\u003cConnect\u003cOwnProps, ContainerProps\u003e\u003e {\n  onClick = () =\u003e {\n    this.props.dispatch(addToFoo(10));\n  }\n  render() {\n    return (\n      \u003cbutton onClick={this.onClick}\u003efoo: {this.props.foo} \u003c/button\u003e\n    );\n  }\n}\n\nexport const MyComponent = connect(_MyComponent, mapStateToProps);\n```\n\nLike react-redux, components need to be mounted within a Provider that injects a store.\n\n```js\n// ./index.js\n\n// @flow\nimport * as React from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport { Provider, Store } from \"./efx\";\nimport { App } from \"./App\";\n\nconst store = new Store();\nReactDOM.render(\n  \u003cProvider store={store}\u003e\n    \u003cApp /\u003e\n  \u003c/Provider\u003e\n);\n```\n\n\n## Testing\n\nWhen testing React apps with Jest + Enzyme, often times we want to wait for some asynchronous computation to complete. Asynchronous actions are inspectable out of the box with a utility method called `.toFinish()`:\n\n\n```js\n// @flow\nimport * as AppActions from \"./app_actions\";\nimport { Store, Provider } from \"./efx\";\nimport { App } from \"./app\";\nimport { mount } from \"enzyme\";\n\ntest(\"displays title\", () =\u003e {\n  const { wrapper } = await mountApp();\n  expect(wrapper.find('h1')).toBe(\"My App\");\n});\n\nasync function mountApp() {\n  const store = new Store();\n  const wrapper = mount(\n    \u003cProvider store={store}\u003e\n      \u003cApp /\u003e\n    \u003c/Provider\u003e\n  );\n\n  await AppActions.initialize.toFinish();\n\n  return { wrapper, store };\n}\n```\n\n```js\n// ./AppActions.js\nimport { type Action, makeAction } from \"./action\";\n\nexport const initialize: Action\u003cvoid, Promise\u003cvoid\u003e\u003e = makeAction(() =\u003e async store =\u003e {\n\n  // put some async computation here, i.e. fetching of data\n\n});\n```\n\n## Why not Redux?\n\nEFX started as a set of opinionated utility functions and classes built on top of redux + react-redux + redux-thunk for a simpler, more testable and more type-safe Redux stack.\n\nInstead of publishing yet another set of opinionated design patterns and utility functions for Redux, we thought that there was enough noise in the ecosystem and that it would provide a better developer experience to put together an all-in-one solution. Therefore, we made the decision to start fresh, borrow the good parts of Redux and write a simpler yet more comprehensive state management solution.\n\nIn other words, EFX seeks to improve the developer experience for state management in the same way that [Jest](https://facebook.github.io/jest/) does for testing by packaging Mocha + Chai + Sinon into a single solution.\n\n### Good parts of Redux:\n\n- Centralized state\n- Unidirectional data flow\n- *With `redux-thunk`:* Compositionality: a way to `dispatch` actions within actions\n- *With `react-redux`:* Simplicity: prop updating based on shallow equality\n\n### Bad parts of Redux:\n\n- Message bus/Reducers: forces you to bend over backwards to change state via messages passing with no return values.\n- Too low level -\u003e Too many abstractions -\u003e Terminology overload (reducers, constants, actions, action creators, middleware, etc.)\n- Out-of-the-box experience very lackluster: (type safety? testability? async actions?)\n- Overwhelming ecosystem, maybe too extensible/flexible.\n- Community flow-typings are abysmal\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleexyz%2Fefx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsleexyz%2Fefx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsleexyz%2Fefx/lists"}