redux-simple-router
Advanced tools
Comparing version 0.0.10 to 1.0.0
135
lib/index.js
@@ -1,7 +0,10 @@ | ||
'use strict'; | ||
"use strict"; | ||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
// constants | ||
var deepEqual = require('deep-equal'); | ||
// Constants | ||
var INIT_PATH = "@@router/INIT_PATH"; | ||
var UPDATE_PATH = "@@router/UPDATE_PATH"; | ||
@@ -12,12 +15,50 @@ var SELECT_STATE = function SELECT_STATE(state) { | ||
// Action creator | ||
// Action creators | ||
function updatePath(path, avoidRouterUpdate) { | ||
function initPath(path, state) { | ||
return { | ||
type: INIT_PATH, | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: false, | ||
avoidRouterUpdate: true | ||
} | ||
}; | ||
} | ||
function pushPath(path, state) { | ||
var _ref = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var _ref$avoidRouterUpdat = _ref.avoidRouterUpdate; | ||
var avoidRouterUpdate = _ref$avoidRouterUpdat === undefined ? false : _ref$avoidRouterUpdat; | ||
return { | ||
type: UPDATE_PATH, | ||
path: path, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: false, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
} | ||
}; | ||
} | ||
function replacePath(path, state) { | ||
var _ref2 = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; | ||
var _ref2$avoidRouterUpda = _ref2.avoidRouterUpdate; | ||
var avoidRouterUpdate = _ref2$avoidRouterUpda === undefined ? false : _ref2$avoidRouterUpda; | ||
return { | ||
type: UPDATE_PATH, | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: true, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
} | ||
}; | ||
} | ||
// Reducer | ||
@@ -27,3 +68,5 @@ | ||
changeId: 1, | ||
path: typeof window !== 'undefined' ? locationToString(window.location) : '/' | ||
path: undefined, | ||
state: undefined, | ||
replace: false | ||
}; | ||
@@ -33,8 +76,12 @@ | ||
var state = arguments.length <= 0 || arguments[0] === undefined ? initialState : arguments[0]; | ||
var action = arguments[1]; | ||
var _ref3 = arguments[1]; | ||
var type = _ref3.type; | ||
var payload = _ref3.payload; | ||
if (action.type === UPDATE_PATH) { | ||
if (type === INIT_PATH || type === UPDATE_PATH) { | ||
return _extends({}, state, { | ||
path: action.path, | ||
changeId: state.changeId + (action.avoidRouterUpdate ? 0 : 1) | ||
path: payload.path, | ||
changeId: state.changeId + (payload.avoidRouterUpdate ? 0 : 1), | ||
state: payload.state, | ||
replace: payload.replace | ||
}); | ||
@@ -47,4 +94,4 @@ } | ||
function locationToString(location) { | ||
return location.pathname + location.search + location.hash; | ||
function locationsAreEqual(a, b) { | ||
return a != null && b != null && a.path === b.path && deepEqual(a.state, b.state); | ||
} | ||
@@ -58,4 +105,12 @@ | ||
}; | ||
var lastChangeId = 0; | ||
// To properly handle store updates we need to track the last route. | ||
// This route contains a `changeId` which is updated on every | ||
// `pushPath` and `replacePath`. If this id changes we always | ||
// trigger a history update. However, if the id does not change, we | ||
// check if the location has changed, and if it is we trigger a | ||
// history update. It's possible for this to happen when something | ||
// reloads the entire app state such as redux devtools. | ||
var lastRoute = undefined; | ||
if (!getRouterState()) { | ||
@@ -66,7 +121,35 @@ throw new Error("Cannot sync router: route state does not exist. Did you " + "install the routing reducer?"); | ||
var unsubscribeHistory = history.listen(function (location) { | ||
var routePath = locationToString(location); | ||
var route = { | ||
path: history.createPath(location), | ||
state: location.state | ||
}; | ||
// Avoid dispatching an action if the store is already up-to-date | ||
if (getRouterState().path !== routePath) { | ||
store.dispatch(updatePath(routePath, { avoidRouterUpdate: true })); | ||
if (!lastRoute) { | ||
// `initialState` *should* represent the current location when | ||
// the app loads, but we cannot get the current location when it | ||
// is defined. What happens is `history.listen` is called | ||
// immediately when it is registered, and it updates the app | ||
// state with an UPDATE_PATH action. This causes problem when | ||
// users are listening to UPDATE_PATH actions just for | ||
// *changes*, and with redux devtools because "revert" will use | ||
// `initialState` and it won't revert to the original URL. | ||
// Instead, we specialize the first route notification and do | ||
// different things based on it. | ||
initialState = { | ||
changeId: 1, | ||
path: route.path, | ||
state: route.state, | ||
replace: false | ||
}; | ||
// Also set `lastRoute` so that the store subscriber doesn't | ||
// trigger an unnecessary `pushState` on load | ||
lastRoute = initialState; | ||
store.dispatch(initPath(route.path, route.state)); | ||
} else if (!locationsAreEqual(getRouterState(), route)) { | ||
// The above check avoids dispatching an action if the store is | ||
// already up-to-date | ||
var method = location.action === 'REPLACE' ? replacePath : pushPath; | ||
store.dispatch(method(route.path, route.state, { avoidRouterUpdate: true })); | ||
} | ||
@@ -78,8 +161,9 @@ }); | ||
// Only update the router once per `updatePath` call. This is | ||
// indicated by the `changeId` state; when that number changes, we | ||
// should call `pushState`. | ||
if (lastChangeId !== routing.changeId) { | ||
lastChangeId = routing.changeId; | ||
history.pushState(null, routing.path); | ||
// Only trigger history update if this is a new change or the | ||
// location has changed. | ||
if (lastRoute.changeId !== routing.changeId || !locationsAreEqual(lastRoute, routing)) { | ||
lastRoute = routing; | ||
var method = routing.replace ? 'replaceState' : 'pushState'; | ||
history[method](routing.state, routing.path); | ||
} | ||
@@ -96,5 +180,6 @@ }); | ||
UPDATE_PATH: UPDATE_PATH, | ||
updatePath: updatePath, | ||
pushPath: pushPath, | ||
replacePath: replacePath, | ||
syncReduxAndRouter: syncReduxAndRouter, | ||
routeReducer: update | ||
}; |
{ | ||
"name": "redux-simple-router", | ||
"version": "0.0.10", | ||
"version": "1.0.0", | ||
"description": "Ruthlessly simple bindings to keep react-router and redux in sync", | ||
@@ -15,4 +15,10 @@ "main": "lib/index", | ||
"scripts": { | ||
"build": "mkdir -p lib && babel ./src/index.js --plugins transform-object-assign --out-file ./lib/index.js", | ||
"test": "mocha --compilers js:babel-core/register --recursive", | ||
"build": "mkdir -p lib && babel ./src/index.js --out-file ./lib/index.js", | ||
"test": "npm run test:node && npm run test:browser", | ||
"test:node": "mocha --compilers js:babel-core/register --recursive ./test/node", | ||
"test:browser": "karma start", | ||
"test:cov": "npm run test:cov:browser && npm run test:cov:node && npm run test:cov:report", | ||
"test:cov:node": "babel-node $(npm bin)/isparta cover $(npm bin)/_mocha report --dir ./coverage/node-coverage -- --recursive ./test/node", | ||
"test:cov:browser": "COVERAGE=true karma start", | ||
"test:cov:report": "$(npm bin)/istanbul report --dir ./coverage --include **/*coverage.json html text", | ||
"prepublish": "npm run build" | ||
@@ -32,2 +38,3 @@ }, | ||
"babel-core": "^6.2.1", | ||
"babel-loader": "^6.2.0", | ||
"babel-plugin-transform-object-assign": "^6.0.14", | ||
@@ -37,5 +44,22 @@ "babel-preset-es2015": "^6.1.2", | ||
"history": "^1.13.1", | ||
"isparta": "^4.0.0", | ||
"isparta-loader": "^2.0.0", | ||
"karma": "^0.13.3", | ||
"karma-chrome-launcher": "^0.2.0", | ||
"karma-coverage": "^0.5.3", | ||
"karma-firefox-launcher": "^0.1.7", | ||
"karma-ie-launcher": "^0.2.0", | ||
"karma-mocha": "^0.2.0", | ||
"karma-mocha-reporter": "^1.0.4", | ||
"karma-safari-launcher": "^0.1.1", | ||
"karma-sourcemap-loader": "^0.3.5", | ||
"karma-webpack": "^1.7.0", | ||
"mocha": "^2.3.4", | ||
"redux": "^3.0.4" | ||
"redux": "^3.0.4", | ||
"redux-devtools": "^2.1.5", | ||
"webpack": "^1.12.9" | ||
}, | ||
"dependencies": { | ||
"deep-equal": "^1.0.1" | ||
} | ||
} |
166
README.md
@@ -1,80 +0,64 @@ | ||
# redux-simple-router | ||
[Redux](https://github.com/rackt/redux) is cool. | ||
[react-router](https://github.com/rackt/react-router) is neat. The | ||
problem is that react-router manages an important piece | ||
of your application state: the URL. If you are using redux, you want | ||
your app state to fully represent your UI; if you snapshotted the app | ||
state, you should be able to load it up later and see the same thing. | ||
[![npm version](https://img.shields.io/npm/v/redux-simple-router.svg?style=flat-square)](https://www.npmjs.com/package/redux-simple-router) [![npm downloads](https://img.shields.io/npm/dm/redux-simple-router.svg?style=flat-square)](https://www.npmjs.com/package/redux-simple-router) | ||
react-router automatically maps the current URL to a path down your | ||
component tree, and continually does so with any URL changes. This is | ||
very useful, but we really want to store this state in redux as well. | ||
**Let react-router do all the work** :sparkles: | ||
The entire state that we are interested in boils down to one thing: | ||
the URL. This is an extremely simple library that just puts the URL in | ||
redux state and keeps it in sync with any react-router changes. | ||
Additionally, you can change the URL via redux and react-router will | ||
change accordingly. | ||
[Redux](https://github.com/rackt/redux) is awesome. [React Router](https://github.com/rackt/react-router) is cool. The problem is that react-router manages an important piece of your application state: the URL. If you are using redux, you want your app state to fully represent your UI; if you snapshotted the app state, you should be able to load it up later and see the same thing. | ||
```js | ||
react-router does a great job of mapping the current URL to a component tree, and continually does so with any URL changes. This is very useful, but we really want to store this state in redux as well. | ||
The entire state that we are interested in boils down to one thing: the URL. This is an extremely simple library that just puts the URL in redux state and keeps it in sync with any react-router changes. Additionally, you can change the URL via redux and react-router will change accordingly. | ||
``` | ||
npm install redux-simple-router | ||
``` | ||
## Isn't there already redux-router? | ||
View the [CHANGELOG](https://github.com/jlongster/redux-simple-router/blob/master/CHANGELOG.md) for recent changes. | ||
[redux-router](https://github.com/rackt/redux-router) is another | ||
project which solves the same problem. However, it's far more complex. | ||
Just look at this code: the whole thing is only 68 lines of JS. | ||
redux-router is much bigger and more complex. | ||
Read the [API docs](#api) farther down this page. | ||
That said, redux-router is a fine project and has features this | ||
doesn't provide. Use it if you like it better. | ||
##### _What about redux-router?_ | ||
The differences between this and redux-router: | ||
[redux-router](https://github.com/rackt/redux-router) is another project which solves the same problem. However, it's far more complex. Take a quick look at [the code for this library](https://github.com/jlongster/redux-simple-router/blob/master/src/index.js)—it's extremely minimal. redux-router is much bigger and more complex. | ||
* Much smaller and simpler. You don't need to learn another library on | ||
top of everything else. | ||
* We encourage direct access of react-router APIs. Need server-side | ||
rendering, or something else advanced? Just read react-router's | ||
docs. | ||
* The piece of state is just a URL string, whereas redux-router stores | ||
the full location object from react-router. | ||
That said, redux-router is a fine project and has features this doesn't provide. Use it if you like it better. | ||
This only depends on the `history.listen` function from react-router | ||
and the `store.getState` and `store.subscribe` functions from redux. | ||
It should be very future-proof with any versions of either libraries. | ||
**Compared with redux-router:** | ||
## How to Use | ||
* Much smaller and simpler. You don't need to learn another library on top of everything else. | ||
* We encourage direct access of react-router APIs. Need server-side rendering, or something else advanced? Just read react-router's docs. | ||
* We only store current URL and state, whereas redux-router stores the entire location object from react-router. | ||
The idea of this library is to use react-router's functionality exactly | ||
like its documentation tells you to. You can access all of its APIs in | ||
routing components. Additionally, you can use redux like you normally | ||
would, with a single app state and "connected" components. It's even | ||
possible for a single component to be both if needed. | ||
### Usage | ||
This library provides a single point of synchronization: the | ||
`routing.path` piece of state which is the current URL. You can read | ||
it, and also change it with an action. | ||
The idea of this library is to use react-router's functionality exactly like its documentation tells you to. You can access all of its APIs in routing components. Additionally, you can use redux like you normally would, with a single app state and "connected" components. It's even possible for a single component to be both if needed. | ||
Here's some code: | ||
[redux](https://github.com/rackt/redux) (`store.routing`) ↔ [**redux-simple-router**](https://github.com/jlongster/redux-simple-router) ↔ [history](https://github.com/rackt/history) (`history.location`) ↔ [react-router](https://github.com/rackt/react-router) | ||
We only store current URL and state, whereas redux-router stores the entire location object from react-router. You can read it, and also change it with an action. | ||
### Tutorial | ||
Let's take a look at a simple example. | ||
```js | ||
const { createStore, combineReducers } = require('redux'); | ||
const { Provider } = require('react-redux'); | ||
const { Router, Route } = require('react-router'); | ||
const createBrowserHistory = require('history/lib/createBrowserHistory'); | ||
const { syncReduxAndRouter, routeReducer } = require('redux-simple-router'); | ||
const reducers = require('<project-path>/reducers'); | ||
import React from 'react' | ||
import ReactDOM from 'react-dom' | ||
import { createStore, combineReducers } from 'redux' | ||
import { Provider } from 'react-redux' | ||
import { Router, Route } from 'react-router' | ||
import { createHistory } from 'history' | ||
import { syncReduxAndRouter, routeReducer } from 'redux-simple-router' | ||
import reducers from '<project-path>/reducers' | ||
const reducer = combineReducers(Object.assign({}, reducers, { | ||
routing: routeReducer | ||
})); | ||
const store = createStore(reducer); | ||
const history = createBrowserHistory(); | ||
})) | ||
const store = createStore(reducer) | ||
const history = createHistory() | ||
syncReduxAndRouter(history, store); | ||
syncReduxAndRouter(history, store) | ||
React.render( | ||
ReactDOM.render( | ||
<Provider store={store}> | ||
@@ -89,24 +73,19 @@ <Router history={history}> | ||
document.getElementById('mount') | ||
); | ||
) | ||
``` | ||
Now you can read from `state.routing.path` to get the URL. It's far | ||
more likely that you want to change the URL more often, however. You | ||
can use the `updatePath` action creator the we provide: | ||
Now you can read from `state.routing.path` to get the URL. It's far more likely that you want to change the URL more often, however. You can use the `pushPath` action creator that we provide: | ||
```js | ||
const { updatePath } = require ('redux-simple-router'); | ||
import { pushPath } from 'redux-simple-router' | ||
function MyComponent({ dispatch }) { | ||
return <Button onClick={() => dispatch(updatePath('/foo'))}/>; | ||
return <Button onClick={() => dispatch(pushPath('/foo'))}/>; | ||
} | ||
``` | ||
This will change the state, which will trigger a change in react-router. | ||
This will change the state, which will trigger a change in react-router. Additionally, if you want to respond to the path update action, just handle the `UPDATE_PATH` constant that we provide: | ||
Additionally, if you want to respond to the path update action, just | ||
handle the `UPDATE_PATH` constant that we provide: | ||
```js | ||
const { UPDATE_PATH } = require('redux-simple-router'); | ||
import { UPDATE_PATH } from 'redux-simple-router' | ||
@@ -121,37 +100,42 @@ function update(state, action) { | ||
For more context, check out this [full example](https://github.com/freeqaz/redux-simple-router-example). | ||
### Examples | ||
## API | ||
* [examples/basic](https://github.com/jlongster/redux-simple-router/blob/master/examples/basic) - basic reference implementation | ||
### `syncReduxAndRouter(history, store, selectRouterState?)` | ||
Examples from the community: | ||
Call this with a react-router and a redux store instance to install | ||
hooks that always keep both of them in sync. When one changes, so will | ||
the other. | ||
* [davezuko/react-redux-starter-kit](https://github.com/davezuko/react-redux-starter-kit) - popular redux starter kit | ||
* **tip**: migrating from redux-router? use [this commit](https://github.com/davezuko/react-redux-starter-kit/commit/db66626ca8a02ecf030a3f7f5a669ac338fd5897) as a reference | ||
* [freeqaz/redux-simple-router-example](https://github.com/freeqaz/redux-simple-router-example) - example implementation | ||
Supply an optional function `selectRouterState` to customize where to | ||
find the router state on your app state. It defaults to `state => | ||
state.routing`, so you would install the reducer under the name | ||
"routing". Feel free to change this to whatever you like. | ||
_Have an example to add? Send us a PR!_ | ||
### `routeReducer` | ||
### API | ||
A reducer function that keeps track of the router state. You need to | ||
add this reducer to your app reducers when creating the store. **The | ||
piece of state must be named `routing`** (unless you provide a custom | ||
`selectRouterState` function). | ||
#### `syncReduxAndRouter(history, store, selectRouterState?)` | ||
### `UPDATE_PATH` | ||
Call this with a react-router and a redux store instance to install hooks that always keep both of them in sync. When one changes, so will the other. | ||
An action type that you can listen for in your reducers. | ||
Supply an optional function `selectRouterState` to customize where to find the router state on your app state. It defaults to `state => state.routing`, so you would install the reducer under the name "routing". Feel free to change this to whatever you like. | ||
### `updatePath(path, noRouterUpdate)` | ||
#### `routeReducer` | ||
An action creator that you can use to update the current URL. Just | ||
pass it a string like `/foo/bar?param=5`. | ||
A reducer function that keeps track of the router state. You must to add this reducer to your app reducers when creating the store. If you do not provide a custom `selectRouterState` function, the piece of state must be named `routing`. | ||
The `noRouterUpdate`, if `true`, will stop react-router from reacting | ||
to this and all future URL changes. Pass `false` to make it start | ||
reacting again. This is useful if replaying snapshots while using the | ||
`forceRefresh` option of the browser history which forces full | ||
reloads. It's a rare edge case. | ||
#### `UPDATE_PATH` | ||
An action type that you can listen for in your reducers to be notified of route updates. | ||
#### `pushPath(path, state, { avoidRouterUpdate = false } = {})` | ||
An action creator that you can use to update the current URL and update the browser history. Just pass it a string like `/foo/bar?param=5` as the `path` argument. | ||
You can optionally pass a state to this action creator to update the state of the current route. | ||
The `avoidRouterUpdate`, if `true`, will stop react-router from reacting to this URL change. This is useful if replaying snapshots while using the `forceRefresh` option of the browser history which forces full reloads. It's a rare edge case. | ||
#### `replacePath(path, state, { avoidRouterUpdate = false } = {})` | ||
An action creator that you can use to replace the current URL without updating the browser history. | ||
The `state` and the `avoidRouterUpdate` parameters work just like `pushPath`. |
128
src/index.js
@@ -0,14 +1,44 @@ | ||
const deepEqual = require('deep-equal'); | ||
// constants | ||
// Constants | ||
const INIT_PATH = "@@router/INIT_PATH"; | ||
const UPDATE_PATH = "@@router/UPDATE_PATH"; | ||
const SELECT_STATE = state => state.routing; | ||
// Action creator | ||
// Action creators | ||
function updatePath(path, avoidRouterUpdate) { | ||
function initPath(path, state) { | ||
return { | ||
type: INIT_PATH, | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: false, | ||
avoidRouterUpdate: true | ||
} | ||
}; | ||
} | ||
function pushPath(path, state, { avoidRouterUpdate = false } = {}) { | ||
return { | ||
type: UPDATE_PATH, | ||
path: path, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: false, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
} | ||
}; | ||
} | ||
function replacePath(path, state, { avoidRouterUpdate = false } = {}) { | ||
return { | ||
type: UPDATE_PATH, | ||
payload: { | ||
path: path, | ||
state: state, | ||
replace: true, | ||
avoidRouterUpdate: !!avoidRouterUpdate | ||
} | ||
} | ||
@@ -19,14 +49,16 @@ } | ||
const initialState = { | ||
let initialState = { | ||
changeId: 1, | ||
path: (typeof window !== 'undefined') ? | ||
locationToString(window.location) : | ||
'/' | ||
path: undefined, | ||
state: undefined, | ||
replace: false | ||
}; | ||
function update(state=initialState, action) { | ||
if(action.type === UPDATE_PATH) { | ||
function update(state=initialState, { type, payload }) { | ||
if(type === INIT_PATH || type === UPDATE_PATH) { | ||
return Object.assign({}, state, { | ||
path: action.path, | ||
changeId: state.changeId + (action.avoidRouterUpdate ? 0 : 1) | ||
path: payload.path, | ||
changeId: state.changeId + (payload.avoidRouterUpdate ? 0 : 1), | ||
state: payload.state, | ||
replace: payload.replace | ||
}); | ||
@@ -39,4 +71,4 @@ } | ||
function locationToString(location) { | ||
return location.pathname + location.search + location.hash; | ||
function locationsAreEqual(a, b) { | ||
return a != null && b != null && a.path === b.path && deepEqual(a.state, b.state); | ||
} | ||
@@ -46,4 +78,12 @@ | ||
const getRouterState = () => selectRouterState(store.getState()); | ||
let lastChangeId = 0; | ||
// To properly handle store updates we need to track the last route. | ||
// This route contains a `changeId` which is updated on every | ||
// `pushPath` and `replacePath`. If this id changes we always | ||
// trigger a history update. However, if the id does not change, we | ||
// check if the location has changed, and if it is we trigger a | ||
// history update. It's possible for this to happen when something | ||
// reloads the entire app state such as redux devtools. | ||
let lastRoute = undefined; | ||
if(!getRouterState()) { | ||
@@ -57,7 +97,35 @@ throw new Error( | ||
const unsubscribeHistory = history.listen(location => { | ||
const routePath = locationToString(location); | ||
const route = { | ||
path: history.createPath(location), | ||
state: location.state | ||
}; | ||
// Avoid dispatching an action if the store is already up-to-date | ||
if(getRouterState().path !== routePath) { | ||
store.dispatch(updatePath(routePath, { avoidRouterUpdate: true })); | ||
if (!lastRoute) { | ||
// `initialState` *should* represent the current location when | ||
// the app loads, but we cannot get the current location when it | ||
// is defined. What happens is `history.listen` is called | ||
// immediately when it is registered, and it updates the app | ||
// state with an UPDATE_PATH action. This causes problem when | ||
// users are listening to UPDATE_PATH actions just for | ||
// *changes*, and with redux devtools because "revert" will use | ||
// `initialState` and it won't revert to the original URL. | ||
// Instead, we specialize the first route notification and do | ||
// different things based on it. | ||
initialState = { | ||
changeId: 1, | ||
path: route.path, | ||
state: route.state, | ||
replace: false | ||
}; | ||
// Also set `lastRoute` so that the store subscriber doesn't | ||
// trigger an unnecessary `pushState` on load | ||
lastRoute = initialState; | ||
store.dispatch(initPath(route.path, route.state)); | ||
} else if(!locationsAreEqual(getRouterState(), route)) { | ||
// The above check avoids dispatching an action if the store is | ||
// already up-to-date | ||
const method = location.action === 'REPLACE' ? replacePath : pushPath; | ||
store.dispatch(method(route.path, route.state, { avoidRouterUpdate: true })); | ||
} | ||
@@ -67,11 +135,14 @@ }); | ||
const unsubscribeStore = store.subscribe(() => { | ||
const routing = getRouterState(); | ||
let routing = getRouterState(); | ||
// Only update the router once per `updatePath` call. This is | ||
// indicated by the `changeId` state; when that number changes, we | ||
// should call `pushState`. | ||
if(lastChangeId !== routing.changeId) { | ||
lastChangeId = routing.changeId; | ||
history.pushState(null, routing.path); | ||
// Only trigger history update if this is a new change or the | ||
// location has changed. | ||
if(lastRoute.changeId !== routing.changeId || | ||
!locationsAreEqual(lastRoute, routing)) { | ||
lastRoute = routing; | ||
const method = routing.replace ? 'replaceState' : 'pushState'; | ||
history[method](routing.state, routing.path); | ||
} | ||
}); | ||
@@ -87,5 +158,6 @@ | ||
UPDATE_PATH, | ||
updatePath, | ||
pushPath, | ||
replacePath, | ||
syncReduxAndRouter, | ||
routeReducer: update | ||
}; |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
702521
265
12672
1
1
23
140
2
56
2
+ Addeddeep-equal@^1.0.1
+ Addedcall-bind@1.0.7(transitive)
+ Addeddeep-equal@1.1.2(transitive)
+ Addeddefine-data-property@1.1.4(transitive)
+ Addeddefine-properties@1.2.1(transitive)
+ Addedes-define-property@1.0.0(transitive)
+ Addedes-errors@1.3.0(transitive)
+ Addedfunction-bind@1.1.2(transitive)
+ Addedfunctions-have-names@1.2.3(transitive)
+ Addedget-intrinsic@1.2.4(transitive)
+ Addedgopd@1.0.1(transitive)
+ Addedhas-property-descriptors@1.0.2(transitive)
+ Addedhas-proto@1.0.3(transitive)
+ Addedhas-symbols@1.0.3(transitive)
+ Addedhas-tostringtag@1.0.2(transitive)
+ Addedhasown@2.0.2(transitive)
+ Addedis-arguments@1.1.1(transitive)
+ Addedis-date-object@1.0.5(transitive)
+ Addedis-regex@1.1.4(transitive)
+ Addedobject-is@1.1.6(transitive)
+ Addedobject-keys@1.1.1(transitive)
+ Addedregexp.prototype.flags@1.5.2(transitive)
+ Addedset-function-length@1.2.2(transitive)
+ Addedset-function-name@2.0.2(transitive)