{"id":18554659,"url":"https://github.com/realmteam/react-redux-reliever","last_synced_at":"2025-04-09T23:31:44.770Z","repository":{"id":19161793,"uuid":"86388057","full_name":"RealmTeam/react-redux-reliever","owner":"RealmTeam","description":"Stop opening four different files to code a simple feature when using redux","archived":false,"fork":false,"pushed_at":"2023-01-04T21:43:25.000Z","size":2936,"stargazers_count":6,"open_issues_count":16,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-14T19:50:19.058Z","etag":null,"topics":["javascript","react","redux","redux-saga"],"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/RealmTeam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-27T21:59:17.000Z","updated_at":"2021-01-11T18:53:01.000Z","dependencies_parsed_at":"2023-01-11T20:30:54.928Z","dependency_job_id":null,"html_url":"https://github.com/RealmTeam/react-redux-reliever","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/RealmTeam%2Freact-redux-reliever","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RealmTeam%2Freact-redux-reliever/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RealmTeam%2Freact-redux-reliever/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RealmTeam%2Freact-redux-reliever/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RealmTeam","download_url":"https://codeload.github.com/RealmTeam/react-redux-reliever/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248129831,"owners_count":21052647,"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","react","redux","redux-saga"],"created_at":"2024-11-06T21:23:17.251Z","updated_at":"2025-04-09T23:31:41.031Z","avatar_url":"https://github.com/RealmTeam.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-redux-reliever\n\n[![npm version](https://img.shields.io/npm/v/react-redux-reliever.svg?style=flat-square)](https://www.npmjs.com/package/react-redux-reliever)\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)\n\nFrom redux's creator himself Dan Abramov :\n\n\u003e [Why is this stuff in five different files and constants SHOUTING at me](https://twitter.com/dan_abramov/status/1191487701058543617)\n\n\n`react-redux-reliever` is a simple package that aims to relieve you from the pain of\nopening multiple different files and write a lot of boring code each time you want to\nadd a small feature in your React app.\n\nThe principle is as follow : regroup all the logic from redux in a single file when\nyou're developing new features while defaulting to certain behaviors to save you some\ntime. You can easily override anything you don't like so you're not stuck either.\n\nIt obviously uses [react](https://github.com/facebook/react) and [redux](https://github.com/reactjs/redux).\n\n\n# Getting started\n\n## Install\n\n```sh\n$ npm install --save react-redux-reliever\n```\nor\n\n```sh\n$ yarn add react-redux-reliever\n```\n\n## Plugins\n * Rx: based on [redux-observable](https://github.com/redux-observable/redux-observable).\n ```javascript\n    import RelieverRegistry, {plugins} from 'react-redux-reliever'\n\n    RelieverRegistry.use(plugins.RxRelieverPlugin)\n ```\n * Saga: based on [redux-saga](https://github.com/redux-saga/redux-saga).\n ```javascript\n    import RelieverRegistry, {plugins} from 'react-redux-reliever'\n\n    RelieverRegistry.use(plugins.SagaRelieverPlugin)\n ```\n\nYou may also create your own plugins using the following interface.\n```javascript\n\tclass SomePlugin {\n    \t\tcreateMiddleware(reliever) {}\n\t        setupStore(store) {}\n\t}\n    \n    RelieverRegistry.use(SomePlugin)\n```\n\n## Usage Example\n\nFirst, import `RelieverRegistry` and `Reliever`\n```javascript\nimport RelieverRegistry, {Reliever} from 'react-redux-reliever'\n```\n\nCreate a class that extends `Reliever`\n```javascript\nclass ComponentReliever extends Reliever {\n```\n\n`ACTION_PREFIX` is used if you want to use the default behavior for the reducer which is\nto check if the action type starts with this prefix and merges the action payload\n(`action.payload = {}`) with the state if that's the case.  \nLeave it out if you don't wish to use the default behavior.\n```javascript\n    ACTION_PREFIX = 'WHATEVER'\n```\n\nThe initial state for the reducer. Note that it will be transformed to a [SeamlessImmutable](https://github.com/rtfeldman/seamless-immutable) object.\n```javascript\n    getInitialState() {\n        return {\n            value: null\n        }\n    }\n```\n\n\n`getActions` is where you define actions that could be used by other containers\n(otherwise, simply use the payload)\n```javascript\n    getActions() {\n        return {\n            doSomething: () =\u003e ({type: 'WHATEVER_ACTION'}),\n            doSomethingElse: () =\u003e ({type: 'WHATEVER_ACTION_ASYNC'})         \n        }\n    }\n```\n\nThe reducer is not required but you can still override it. Here we have a mix between\nthe default `react-redux-reliever` behavior and the standard `redux` behavior.  \nDon't forget that the state is a [SeamlessImmutable](https://github.com/rtfeldman/seamless-immutable/) object.  \nThe most frequent case for a mixed reducer is something needing to be added to some\nvalue in the state.\n```javascript\n    reducer(state, action) {\n        switch (action.type) {\n            case 'WHATEVER_ADD':\n                return state.set('value', state.value + \"!\")\n            default:\n                // Don't forget to call super\n                return super.reducer(state, action)\n        }\n    }\n}\n\nexport default ComponentReliever\n```\n\n\n## Using Rx\n\nCreate your `epics` (see [redux-observable](https://github.com/redux-observable/redux-observable)). All methods that have a name ending with 'Epic' will be used.\n```javascript\n\n    import {Reliever} from 'react-redux-reliever'\n    import {flatMap} from 'rxjs/operators'\n    import {defer, map, mapTo} from 'rxjs'\n\n    class SomeReliever extends Reliever {\n\n        someEpic(action$) {\n            return action$\n                .ofType('WHATEVER_ACTION') // takes every action of type 'WHATEVER_ACTION' from the action stream\n                .pipe(mapTo({type: 'WHATEVER_UPDATE', payload: {value: 'foo'}})) // then maps the action to an action of type 'WHATEVER_UPDATE'. payload will be applied to the state automatically without using a reducer\n        }\n\n        // you can also easily handle async actions\n        someAsyncEpic(action$) {\n            return action$\n                .ofType('WHATEVER_ACTION_ASYNC')\n                .pipe(\n                  flatMap(action =\u003e\n                    defer(async () =\u003e {\n                      const result = await fetch(`https://some-api.com/foo?userId=${action.userId}`)\n                      return await result.json()\n                    })\n                  ),\n                  map(json =\u003e ({\n                     type: 'WHATEVER_USER_DATA_FETCHED',\n                     payload: {\n                       userData: json.foo\n                     }\n                  }))\n                )\n        }\n    }\n```\n\n`RxRelieverPlugin` also provides extensions for [rxjs](https://github.com/reactivex/rxjs)\nto provide you with convenient methods to access and observe the `store` and `state`\n```javascript\n    import {plugins} from 'react-redux-reliever'\n    const extensions = plugins.RxRelieverPlugin.extensions()\n    extensions.getStore() // store observable, triggers once upon subscription\n    extensions.getState() // state observable, triggers once upon subscription\n    extensions.getState('substate') // substate observable, triggers once upon subscription\n    extensions.observeState() // state observable, triggers when the state changes\n    extensions.observeState('substate') // substate observable, triggers when the state changes\n    extensions.reduxActionStream() // returns the global action stream\n```\n\nThis allows you to build complex sequences of actions while leveraging the flexibility\nand operators of [rxjs](https://github.com/reactivex/rxjs)\n\n```javascript\n    fooEpic(action$) {\n        const shouldStop$ = extensions.observeState('substate').pipe(\n            map(state =\u003e state.toMutable().someProp),\n            filter(prop =\u003e prop === 'foo'), // this observable will trigger when the property someProp === 'foo'\n            take(1) // unsubscribe once the filter operator has triggered\n        )\n\n        return action$.ofType('WHATEVER_FOO_ACTION').pipe(\n            mapTo({type: 'WHATEVER_SOMETHING_ELSE'}),\n            takeUntil(shouldStop$)\n        )\n    }\n```\n\n## Using saga\n\nadd a `*saga()` method to your reliever and add all your saga logic there\n\n```javascript\nimport {takeLatest} from 'redux-saga/effects'\n\nclass MyReliever extends Reliever {\n\t*handleSomeAction(action) {\n\t\t// do something\n\t}\n\t\n\t*saga() {\n\t\tyield takeLatest('SOME_ACTION', this.handleSomeAction.bind(this))\n\t}\n}\n```\n\n## Adding the store\n\nin your store file\n```javascript\nimport RelieverRegistry, {plugins} from \"react-redux-reliever\"\n```\nWe can register our plugins and reliever(s) to the registry\n```javascript\nRelieverRegistry.register(ComponentReliever, \"whatever\")\n\nRelieverRegistry.use(plugins.RxRelieverPlugin)\nRelieverRegistry.use(plugins.SagaRelieverPlugin)\nRelieverRegistry.use(MyOwnPlugin)\n```\n\nWe can then use the registry to create the store and rootReducer like so\n```javascript\n\nconst rootReducer = RelieverRegistry.buildRootReducer()\n\n// You can pass an object to include other reducers you may have\n// By default everything will be on the same level in your store but you can pass\n// an extra argument to put reducers from the registry on another level\nconst rootReducer = RelieverRegistry.buildRootReducer({\n    otherReducer: myOtherReducer\n}, \"customLevelInStore\")\n\nconst store = createStore(RelieverRegistry.buildRootReducer(), applyMiddleware(...RelieverRegistry.middlewares(), logger))\nRelieverRegistry.setupStore(store)\n```\n\nNow you can connect your component to the store.  \n\nYou can choose to do it the usual way or you can use our custom `connect` function.\nIn your `Component.js`, we import the necessary functions\n```javascript\nimport RelieverRegistry, {connect} from \"react-redux-reliever\"\n```\n\nThe connect function takes two named parameters : `props` and `functions`\n\n`props` is exactly like `mapStateToProps` except that you also need to return `ownProps`\n(that way you are able to easily remove unwanted props)\n```javascript\n    props(state, ownProps) {\n        // moduleState is used to retrieve the module's state in the whole store\n        return {value: RelieverRegistry.moduleState(\"whatever\", state).get('value'), ...ownProps}\n    }\n```\n\n`functions` is the same as `mapDispatchToProps` except that you have access to all the props returned by `props`.  \n```javascript\n    functions(ownProps, dispatch) {\n        return {\n            test: () =\u003e {\n                // Note that we generally don't use action creators.\n                // This is purely a choice and you can still use them if you want.\n                dispatch({type: 'WHATEVER_TEST', payload: {value: \"Looking good !\"}})\n            },\n            doSomething: () =\u003e {\n                // If you want to use action creators from a module, you could do so like that by using its name\n                dispatch(RelieverRegistry.moduleActions(\"whatever\").doSomething())\n            }\n        }\n    }\n```\n\nIn the end, `connect` is used like that\n```javascript\nexport default connect({\n    props: (state, ownProps) =\u003e {/* Your code here */},\n    functions: (ownProps, dispatch) =\u003e {/* Your code here */}\n})(Component)\n```\n\nThat's it !\n\n# Building examples from sources\n\n```sh\n$ git clone https://github.com/aronse/react-redux-reliever.git\n$ cd react-redux-reliever\n$ npm install\n```\n\nAs of today, only a simple counter example has been implemented.\n\n### Counter example\n\n```sh\n$ npm run counter\n```\n\n# Contributing\n\nFeel free to open issues and submit pull-requests.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frealmteam%2Freact-redux-reliever","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frealmteam%2Freact-redux-reliever","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frealmteam%2Freact-redux-reliever/lists"}