{"id":20924510,"url":"https://github.com/ericdouglas/onli-reducer","last_synced_at":"2025-05-13T16:31:13.890Z","repository":{"id":35087060,"uuid":"204906895","full_name":"ericdouglas/onli-reducer","owner":"ericdouglas","description":"⚛️ One line reducer. State management without boilerplate.","archived":false,"fork":false,"pushed_at":"2023-01-04T08:31:45.000Z","size":1560,"stargazers_count":34,"open_issues_count":16,"forks_count":3,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-24T07:06:26.427Z","etag":null,"topics":["javascript","javascript-library","react","redux","state-management","state-management-in-react"],"latest_commit_sha":null,"homepage":"https://ericdouglas.github.io/onli-reducer/","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/ericdouglas.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-28T10:33:24.000Z","updated_at":"2024-07-18T20:27:42.000Z","dependencies_parsed_at":"2023-01-15T13:45:16.330Z","dependency_job_id":null,"html_url":"https://github.com/ericdouglas/onli-reducer","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericdouglas%2Fonli-reducer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericdouglas%2Fonli-reducer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericdouglas%2Fonli-reducer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericdouglas%2Fonli-reducer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericdouglas","download_url":"https://codeload.github.com/ericdouglas/onli-reducer/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253981753,"owners_count":21994328,"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":["javascript","javascript-library","react","redux","state-management","state-management-in-react"],"created_at":"2024-11-18T20:22:51.620Z","updated_at":"2025-05-13T16:31:08.881Z","avatar_url":"https://github.com/ericdouglas.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# onli-reducer\n\n\u003e onli -\u003e **on**e **li**ne reducer\n\n[![Codeship Status for ericdouglas/onli-reducer](https://app.codeship.com/projects/c69cbcf0-ae10-0137-49b6-5aae0d4b4bf9/status?branch=master)](https://app.codeship.com/projects/362530)\n[![Coverage Status](https://coveralls.io/repos/github/ericdouglas/onli-reducer/badge.svg?branch=master)](https://coveralls.io/github/ericdouglas/onli-reducer?branch=master)\n[![npm version](https://badge.fury.io/js/onli-reducer.svg)](https://badge.fury.io/js/onli-reducer)\n\n[![NPM](https://nodei.co/npm/onli-reducer.png)](https://nodei.co/npm/onli-reducer/)\n\n**onli-reducer** is a micro-library that helps you to write applications using redux/useReducer without boilerplate.\n\n\u003e Works with whatever state management that gives you a `dispatch` method and expects you to call such method passing an object to it with a `type` property.\n\n## Table of Contents\n\n- [onli-reducer](#onli-reducer)\n  - [Table of Contents](#table-of-contents)\n  - [Benefits over traditional redux/useReducer usage (with `switch`)](#benefits-over-traditional-reduxusereducer-usage-with-switch)\n  - [Install](#install)\n  - [Basic Concepts](#basic-concepts)\n    - [Reducer Solution](#reducer-solution)\n    - [Dispatch Solution](#dispatch-solution)\n  - [Usage / Examples](#usage--examples)\n    - [Using onli-reducer with React Hooks + Context](#using-onli-reducer-with-react-hooks--context)\n    - [Using onli-reducer with Redux](#using-onli-reducer-with-redux)\n  - [Conventions](#convetions)\n  - [API](#api)\n    - [`onli(actions)`](#onliactions)\n    - [`onliSend(dispatch, types)`](#onlisenddispatch-types)\n    - [`onliReducer(actions)`](#onlireduceractions)\n    - [`onliTypes(actions)`](#onlitypesactions)\n  - [License](#license)\n\n## Benefits over traditional redux/useReducer usage (with `switch`)\n\n- No boilerplate code.\n- _Literally_ one line reducer (no need to use `switch` statement).\n- Direct call to actions without instructions about their `type`.\n- Support for asynchronous calls (dispatch/send actions from async functions).\n- No need to manually write your types.\n\n## Install\n\n```sh\nnpm install --save onli-reducer\n```\n\n## Basic Concepts\n\n**onli-reducer** expose 4 methods in order to help you eliminate boilerplate code from your reducers, dispatch calls and application in general.\n\n\u003e Although we expose 4 methods, you will normally just use the `onli` and `onliSend` method.\n\n### Reducer Solution\n\nInstead of create a reducer function with an evergrowing `switch` statement inside of it, you just need to:\n\n1. Declare your actions as plain JavaScript functions;\n2. Attach such functions to an `actions` object;\n3. Pass the `actions` object to `onli()` helper.\n\nThe `onli` helper method returns an array with 2 elements: a reducer function and an array of strings (types), that is generated based on your actions' names.\n\n**Example**:\n\n```js\nimport onli from \"onli-reducer\"\n\nconst increment = state =\u003e state + 1\nconst decrement = state =\u003e state - 1\n\nconst actions = { increment, decrement }\nconst [countReducer, types] = onli(actions)\n\nexport { countReducer, types }\n```\n\n### Dispatch Solution\n\nIstead of manually set the type in _every_ dispatch call, you can just:\n\n1. Pass your `dispatch` method (from Redux or useReducer) and `types` array (from `onli`) for `onliSend`.\n2. Call your actions directly.\n\n---\n\n**Important**:\n\nAll your actions will receive in the `action` object dispatched at least two properties: `type` and `send`. You can pass any additional payload to your actions.\n\nThe `type` property is a string to let your reducer know which function it has to invoke.\n\nThe `send` property is an object that holds all other public actions so you can pass it to async functions in order to update your state after async calls.\n\nYou can see how it is used in a real app [here](#using-onli-reducer-with-react-hooks--context).\n\n---\n\n**Example**:\n\n```jsx\nimport { onliSend } from \"onli-reducer\"\nimport { countReducer, types } from \"./count.reducer\"\n\n// ...\n\nconst send = onliSend(dispatch, types)\nconst { increment, decrement } = send\n\n// You can also:\n// const { increment, decrement } = onliSend(dispatch, types)\n\n// ...\n\n\u003cCounter\n  value={count}\n  onIncrement={() =\u003e increment()}\n  onDecrement={() =\u003e decrement()}\n/\u003e\n```\n\n## Usage / Examples\n\n### Using onli-reducer with React Hooks + Context\n\nSee a full example with asynchronous calls so you can have a glimpse of how **onli-reducer** would perform in real-world applications.\n\nIn such example app you will see:\n\n- data fetching\n- loading state transition\n- React hooks `useState`, `useReducer` and `useContext`\n- React Context API\n- asynchronous actions called from your reducer\n- more...\n\nSee the [live demo here](https://ericdouglas.github.io/onli-reducer-example/) and the [source code here](https://github.com/ericdouglas/onli-reducer-example).\n\n### Using onli-reducer with Redux\n\nTo show a comparison between the traditional usage of Redux and the new approach using **onli-reducer**, let's rebuild the _Counter_ app from [Redux docs](https://redux.js.org/introduction/examples#counter).\n\n\u003e Obs: it will be shown only the differences between the two approaches.\n\n`index.js` with Redux:\n\n```js\n// ...\nimport counter from \"./reducers\"\n\nconst store = createStore(counter)\n\n// ...\n\n\u003cCounter\n  value={store.getState()}\n  onIncrement={() =\u003e store.dispatch({ type: \"INCREMENT\" })}\n  onDecrement={() =\u003e store.dispatch({ type: \"DECREMENT\" })}\n/\u003e\n```\n\n`index.js` with Redux + **onli-reducer**:\n\n```js\n// ...\nimport { createStore } from \"redux\"\nimport { onliSend } from \"onli-reducer\"\nimport { countReducer, types } from \"./reducers\"\n\nconst store = createStore(countReducer, 0)\nconst send = onliSend(store.dispatch, types)\nconst { increment, decrement } = send\n\n// ...\n\n\u003cCounter\n  value={store.getState()}\n  onIncrement={() =\u003e increment()}\n  onDecrement={() =\u003e decrement()}\n/\u003e\n\n```\n\n`reducers/index.js` with Redux:\n\n```js\nexport default (state = 0, action) =\u003e {\n  switch (action.type) {\n    case \"INCREMENT\":\n      return state + 1\n    case \"DECREMENT\":\n      return state - 1\n    default:\n      return state\n  }\n}\n```\n\n`reducers/index.js` with Redux + **onli-reducer**:\n\n```js\nimport onli from \"onli-reducer\"\n\nconst increment = state =\u003e state + 1\nconst decrement = state =\u003e state - 1\n\nconst actions = { increment, decrement }\nconst [countReducer, types] = onli(actions)\n\nexport { countReducer, types }\n```\n\nYou can see/edit the example above here:\n\n[![Edit counter](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/counter-c6vy4?fontsize=14)\n\n\u003e **OBS**: although in this simple example it can not be so evident the benefits of using onli-reducer, for real-world applications the amount of boilerplate code that onli-reducer helps you to **not** type is considerable.\n\n## Conventions\n\nInside `your.reducer.js` file, attach your synchronous functions to the `actions` object and name your asynchronous functions with an underscore, so you know they are both private and async.\n\nThese async functions will be triggered from your sync ones, and after finish their job such async functions will be able to dispatch sync functions to update the state.\n\n**It is a good practice to keep your reducer \"pure\", only dealing with sync functions.**\n\nYou can see how it is implemented in our [example app](#using-onli-reducer-with-react-hooks--context).\n\n## API\n\n### `onli(actions)`\n\nThe `onli` method expects an object that contains your public/synchrounous actions. It returns an array with two elements: a reducer (`function`) and an array with strings that represents your types (`[string]`).\n\n```js\nimport onli from \"onli-reducer\"\n\nconst increment = state =\u003e state + 1\nconst decrement = state =\u003e state - 1\n\nconst actions = { increment, decrement }\n\nconst [countReducer, types] = onli(actions)\n\nexport { countReducer, types }\n```\n\n### `onliSend(dispatch, types)`\n\nThe `onliSend` method receives a `dispatch` function and a `types` array of strings.\n\nIt will return an object with methods attached to it. You will use these methods to dispatch actions in order to update your state.\n\n```js\n///// Without onli-reducer\ndispatch({ type: \"increment\" })\ndispatch({ type: \"increment\", step: 5 })\n\n///// With onli-reducer\nincrement()\nincrement({ step: 5 })\n```\n\n`onliSend` also adds to your `action` payload an object called `send`, that holds itself, the object returned from calling `onliSend(dispatch, types)` that have access to all your actions.\n\nThis is very useful because with such object you can pass it for async functions so them can dispatch actions to update your state after finish their async tasks.\n\n```js\n// async/private action from your reducer\nconst _getPokemon = async ({ name, send }) =\u003e {\n  const { showLoading, hideLoading, updateStore } = send // \u003c- access to your sync methods\n\n  showLoading()\n\n  try {\n    const { data } = await axios.get(`${URL}${name}`)\n    updateStore({ pokemon: data }) // \u003c- update your state after success\n    hideLoading()\n  } catch (error) {\n    updateStore({ warning: \"Ops... Pokémon not found\" }) // \u003c- update your state after failure\n    hideLoading()\n  }\n}\n```\n\n### `onliReducer(actions)`\n\nReceives an `actions` object and return a reducer.\n\n### `onliTypes(actions)`\n\nReceives an `actions` object and return an array with strings (types).\n\n## License\n\n[MIT License](https://ericdouglas.mit-license.org/) © Eric Douglas\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericdouglas%2Fonli-reducer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericdouglas%2Fonli-reducer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericdouglas%2Fonli-reducer/lists"}