{"id":13422429,"url":"https://github.com/salvoravida/redux-first-history","last_synced_at":"2025-05-14T12:11:45.545Z","repository":{"id":38631739,"uuid":"150501079","full_name":"salvoravida/redux-first-history","owner":"salvoravida","description":"Redux history binding support react-router - @reach/router - wouter - react-location","archived":false,"fork":false,"pushed_at":"2025-02-18T10:11:05.000Z","size":746,"stargazers_count":449,"open_issues_count":14,"forks_count":34,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-04-11T05:00:02.756Z","etag":null,"topics":["connected","connected-react-router","connectedrouter","data-flow","first","history","history-redux","reach","reachrouter","react","react-redux","react-redux6","react-router","react-router-redux","redux","router","synchronization","truth","withrouter","wouter"],"latest_commit_sha":null,"homepage":"https://wvst19.csb.app/","language":"TypeScript","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/salvoravida.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-26T23:20:18.000Z","updated_at":"2025-03-30T17:42:57.000Z","dependencies_parsed_at":"2023-12-14T18:29:54.966Z","dependency_job_id":"2f93d045-9649-4964-918a-223136142dbb","html_url":"https://github.com/salvoravida/redux-first-history","commit_stats":{"total_commits":123,"total_committers":13,"mean_commits":9.461538461538462,"dds":0.1869918699186992,"last_synced_commit":"cb621ed84f770a897171a54e58eab25b027a66da"},"previous_names":[],"tags_count":32,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salvoravida%2Fredux-first-history","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salvoravida%2Fredux-first-history/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salvoravida%2Fredux-first-history/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/salvoravida%2Fredux-first-history/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/salvoravida","download_url":"https://codeload.github.com/salvoravida/redux-first-history/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254140768,"owners_count":22021220,"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":["connected","connected-react-router","connectedrouter","data-flow","first","history","history-redux","reach","reachrouter","react","react-redux","react-redux6","react-router","react-router-redux","redux","router","synchronization","truth","withrouter","wouter"],"created_at":"2024-07-30T23:00:44.618Z","updated_at":"2025-05-14T12:11:45.483Z","avatar_url":"https://github.com/salvoravida.png","language":"TypeScript","funding_links":[],"categories":["Code Design","Routing"],"sub_categories":["Router"],"readme":"# redux-first-history\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/redux-first-history\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/redux-first-history.svg?style=flat-square\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://www.npmjs.com/package/redux-first-history\"\u003e\u003cimg src=\"https://img.shields.io/npm/dt/redux-first-history.svg?style=flat-square\"\u003e\u003c/a\u003e\n  \u003cimg src=\"./cov-badge.svg\"\u003e\u003c/a\u003e\u003cbr/\u003e\n  Redux First History - Make Redux 100% SINGLE-AND-ONLY source of truth!\n\u003c/p\u003e\n\nRedux history binding for\n* [`react-router`](https://github.com/remix-run/react-router)\n* [`@reach/router`](https://github.com/reach/router)\n* [`wouter`](https://github.com/molefrog/wouter)\n* [`react-location`](https://github.com/tanstack/react-location)\n* Mix `react-router` - `@reach/router` - `wouter` - `react-location` in the same app! See [Demo](#demo).\n\nCompatible with `immer` - `redux-immer` - `redux-immutable`.\n\n:tada: A smaller, faster, optionated, issue-free alternative to \n[`connected-react-router`](https://github.com/supasate/connected-react-router/issues)\n\n## Table of Contents\n\n- [Main Goal](#main-goal)\n  - [Demo](#demo)\n- [Main Features](#main-features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [Options](#options)\n- [Advanced Config](#advanced-config)\n- [Feedback](#feedback)\n- [Credits \u0026 Inspiration](#credits--inspiration)\n- [Contributors](#contributors)\n- [License](#license)\n\n\n## Main Goal\nWhile working with *relatively large* projects, it's quite common to use both `redux` and `react-router`.\n\nSo you may have components that take location from the redux store, others that take location from router context, and others from withRouter HOC.\n\nThis can generate sync issues, due to the fact that many components are updated at different times.\nIn addition, React shallowCompare rendering optimization will not work as it should.\n\nWith `redux-first-history`, you can mix components that get history from wherever, \nthey will always be tunneled to *state.router.location* !\n\n*Use whatever you like. History will just work as it should.*\n\n```javascript\n//react-router v5 - v6\nuseLocation() === state.router.location\n\n//react-router v5\nthis.props.history.location === state.router.location\nthis.props.location === state.router.location\nwithRouter.props.location === state.router.location\n\n//react-router v4\nthis.context.router.history.location === state.router.location\nthis.context.route.location === state.router.location\n\n//@reach/router\nthis.props.location === state.router.location\n\n//wouter - pathname\nuseLocation()[0] === state.router.location.pathname\n```\n\nMix redux, redux-saga, react-router, @reach/router, wouter and react-location\nwithout any synchronization issue! \u003cbr\u003e\nWhy? Because there is no synchronization at all! There is only one history: reduxHistory!\n* One way data-flow\n* One unique source of truth\n* No more location issues!\n\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"Edit Redux-First Router Demo\" src=\"https://i.postimg.cc/HnxxYzmz/Untitled_Diagram.png\"\u003e\n\u003c/p\u003e\n\n## Demo\n\n- react-router v6: https://wvst19.csb.app/ \n  - Source: https://codesandbox.io/s/redux-first-history-demo-rr6-forked-wvst19\n\n- react-router v5: https://wy5qw1125l.codesandbox.io/ \n  - Source: https://codesandbox.io/s/wy5qw1125l\n\n# Main Features\n \n* 100% one source of truth (store)\n* No synchronization depending on rendering lifecycle (ConnectedRouter)\n* No React dependency (we want history to be always in store!)\n* 100% one-way data flow (only dispatch actions!)\n* Improve React shallowCompare as there is only one \"location\"\n* Support react-location 3.x\n* Support react-router v4 / v5 / v6\n* Support @reach/router 1.x\n* Support wouter 2.x\n* Support mix react-router, @reach/router \u0026 wouter in the same app!\n* Fast migration from an existing project, with the same `LOCATION_CHANGE` and push actions (taken from RRR)\n* Handle Redux Travelling from dev tools (that's nonsense in production, but at the end of the day this decision it's up to you ...)\n\n\n## Installation\nUsing [npm](https://www.npmjs.com/):\n\n    $ npm install --save redux-first-history\n\nOr [yarn](https://yarnpkg.com/):\n\n    $ yarn add redux-first-history\n\n## Usage\n\nstore.js\n\n```javascript\nimport { createStore, combineReducers, applyMiddleware } from \"redux\";\nimport { composeWithDevTools } from \"redux-devtools-extension\";\nimport { createReduxHistoryContext, reachify } from \"redux-first-history\";\nimport { createWouterHook } from \"redux-first-history/wouter\";\nimport { createBrowserHistory } from 'history';\n\nconst { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ \n  history: createBrowserHistory(),\n  //other options if needed \n});\n\nexport const store = createStore(\n  combineReducers({\n    router: routerReducer\n    //... reducers //your reducers!\n  }),\n  composeWithDevTools(\n    applyMiddleware(routerMiddleware)\n  )\n);\n\nexport const history = createReduxHistory(store);\n//if you use @reach/router \nexport const reachHistory = reachify(history);\n//if you use wouter\nexport const wouterUseLocation = createWouterHook(history);\n```\n\nstore.js (with @reduxjs/toolkit)\n```javascript\nimport { combineReducers } from \"redux\";\nimport { configureStore } from \"@reduxjs/toolkit\";\nimport { createReduxHistoryContext } from \"redux-first-history\";\nimport { createBrowserHistory } from \"history\";\n\nconst {\n  createReduxHistory,\n  routerMiddleware,\n  routerReducer\n} = createReduxHistoryContext({ history: createBrowserHistory() });\n\nexport const store = configureStore({\n  reducer: combineReducers({\n    router: routerReducer\n  }),\n  middleware: (getDefaultMiddleware) =\u003e getDefaultMiddleware().concat(routerMiddleware),\n});\n\nexport const history = createReduxHistory(store);\n```\n\napp.js \n```javascript\nimport React, { Component } from \"react\";\nimport { Provider, connect } from \"react-redux\";\nimport { Router } from \"react-router-dom\";\nimport { store, history } from \"./store\";\n\nconst App = () =\u003e (\n      \u003cProvider store={store}\u003e\n        \u003cRouter history={history}\u003e\n        //.....\n        \u003c/Router\u003e\n      \u003c/Provider\u003e\n    );\n\nexport default App;\n```\n\napp.js (react-router v6)\n```javascript\nimport React, { Component } from \"react\";\nimport { Provider } from \"react-redux\";\nimport { HistoryRouter as Router } from \"redux-first-history/rr6\";\nimport { store, history } from \"./store\";\n\nconst App = () =\u003e (\n      \u003cProvider store={store}\u003e\n        \u003cRouter history={history}\u003e\n        //.....\n        \u003c/Router\u003e\n      \u003c/Provider\u003e\n    );\n\nexport default App;\n```\n\n\nsaga.js (react-saga)\n```javascript\nimport { put } from \"redux-saga/effects\";\nimport { push } from \"redux-first-history\";\n\nfunction* randomFunction() {\n  //....\n  yield put(push(YOUR_ROUTE_PATH));\n  //....\n}\n```\n\nslice.js (in a Thunk with @reduxjs/toolkit)\n```javascript\nimport { push } from \"redux-first-history\";\n\nexport const RandomThunk = (dispatch) =\u003e {\n  //....\n  dispatch(push(YOUR_ROUTE_PATH));\n  //....\n}\n```\n\n\n* Just a simple Router with no more ConnectedRouter!\n* Probably, you already did connect the Redux store with `react-router-redux` or `connected-react-router` (in this case you have only to replace the import!)\n\n\n# Options\n\n```javascript 1.8\nexport const createReduxHistoryContext = ({\n  history, \n  routerReducerKey = 'router', \n  reduxTravelling = false, \n  selectRouterState = null,\n  savePreviousLocations = 0,\n  batch = null,\n  reachGlobalHistory = null\n})\n```\n\n|key\t| optional |description   \t| \n|---\t|---|---\t|\n|history\t| no| The `createBrowserHistory` object - v4.x/v5.x  \t| \n|routerReducerKey | yes | if you don't like `router` name for reducer.\n|reduxTravelling | yes | if you want to play with redux-dev-tools :D.\n|selectRouterState |yes | custom selector for router state. With redux-immutable selectRouterState = state =\u003e state.get(\"router\") \n|savePreviousLocations |yes | if \u003e 0 add the key \"previousLocation\" to state.router, with the last N locations. [{location,action}, ...]\n|batch |yes | a batch function for batching states updates with history updates. Prevent top-down updates on react : usage `import { unstable_batchedUpdates } from 'react-dom'; `  `batch = unstable_batchedUpdates`\n|reachGlobalHistory |yes | globalHistory object from  `@reach/router` - support imperatively `navigate` of @reach/router  - `import { navigate } from '@reach/router'`  : usage `import { globalHistory } from '@reach/router'; `  `reachGlobalHistory = globalHistory`\n|basename\t| no| support basename (history v5 fix)  \t| \n\n# Advanced Config\n\n* Support \"navigate\" from @reach/router\n```javascript\nimport { createReduxHistoryContext, reachify } from \"redux-first-history\";\nimport { createBrowserHistory } from 'history';\nimport { globalHistory } from '@reach/router';\n\nconst { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ \n  history: createBrowserHistory(),\n  reachGlobalHistory: globalHistory,\n  //other options if needed \n});\n```\n\n* React batch updates: top-down batch updates for maximum performance. Fix also some updated edge cases.\n```javascript\nimport { createReduxHistoryContext, reachify } from \"redux-first-history\";\nimport { createBrowserHistory } from 'history';\nimport { unstable_batchedUpdates } from 'react-dom';\n\nconst { createReduxHistory, routerMiddleware, routerReducer } = createReduxHistoryContext({ \n  history: createBrowserHistory(),\n  batch: unstable_batchedUpdates,\n  //other options if needed \n});\n```\n# Feedback\n\nLet me know what do you think! \u003cbr\u003e\n*Enjoy it? Star this project!* :D\n\n# Credits \u0026 Inspiration\n - redux-first-routing\n - react-router-redux\n - connected-react-router\n\n# Contributors\nSee [Contributors](https://github.com/salvoravida/redux-first-history/graphs/contributors).\n\n# License\n[MIT License](https://github.com/salvoravida/redux-first-history/blob/master/LICENSE.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalvoravida%2Fredux-first-history","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsalvoravida%2Fredux-first-history","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsalvoravida%2Fredux-first-history/lists"}