Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link method which prevent same locations in the history #570

Closed
wants to merge 13 commits into from
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
- Add `history.link` which navigates and prevents same paths in the history stack (thanks @dabit1)

## [v4.6.3]
> Jun 20, 2017

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,10 @@ The `action` is one of `PUSH`, `REPLACE`, or `POP` depending on how the user got
* `history.goBack()`
* `history.goForward()`
* `history.canGo(n)` (only in `createMemoryHistory`)
* `history.link(path, [state])`
dabit1 marked this conversation as resolved.
Show resolved Hide resolved

When using `push` or `replace` you can either specify both the URL path and state as separate arguments or include everything in a single location-like object as the first argument.
When requiring an action to behave like a link (not pushing duplicate paths to the stack) you can use the `link` method.

1. A URL path _or_
2. A location-like object with `{ pathname, search, hash, state }`
Expand Down
6 changes: 6 additions & 0 deletions modules/LocationUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,9 @@ export const locationsAreEqual = (a, b) =>
a.hash === b.hash &&
a.key === b.key &&
valueEqual(a.state, b.state);

export const shouldReplace = (location, newPath, newState) => {
const currentPath = location.pathname + location.search + location.hash;

return currentPath === newPath && (!location.state || valueEqual(location.state, newState));
}
70 changes: 69 additions & 1 deletion modules/__tests__/LocationUtils-test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import expect from "expect";
import { createLocation } from "../LocationUtils";
import { createLocation, shouldReplace } from "../LocationUtils";

describe("createLocation", () => {
describe("with a full path", () => {
Expand Down Expand Up @@ -160,3 +160,71 @@ describe("createLocation", () => {
});
});
});

describe("shouldReplace", () => {
it("should return true if current path is the same as new path", () => {
const currentLocation = {
pathname: "the/path",
search: "?the=query",
hash: "#the-hash"
};
expect(shouldReplace(currentLocation, 'the/path?the=query#the-hash')).toBeTruthy();
});

it("should return false if current path is other than new path", () => {
const currentLocation = {
pathname: "the/path",
search: "?the=query",
hash: "#the-hash"
};
expect(shouldReplace(currentLocation, 'the/path?the=query')).toBeFalsy();
});

it("should return true if current path is the same as new path and current state is the same as new state", () => {
const currentLocation = {
pathname: "the/path",
search: "?the=query",
hash: "#the-hash",
state: {
foo: 'bar',
obj: {
foo2: 'var2'
}
}
};
expect(shouldReplace(
currentLocation,
'the/path?the=query#the-hash',
{
foo: 'bar',
obj: {
foo2: 'var2'
}
}
)).toBeTruthy();
});

it("should return false if current path is the same as new path and current state is other than new state", () => {
const currentLocation = {
pathname: "the/path",
search: "?the=query",
hash: "#the-hash",
state: {
foo: 'bar',
obj: {
foo2: 'var2'
}
}
};
expect(shouldReplace(
currentLocation,
'the/path?the=query#the-hash',
{
foo: 'bar',
obj: {
foo2: 'var3'
}
}
)).toBeFalsy();
});
});
6 changes: 5 additions & 1 deletion modules/createBrowserHistory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import warning from "warning";
import invariant from "invariant";
import { createLocation } from "./LocationUtils";
import { createLocation, shouldReplace } from "./LocationUtils";
import {
addLeadingSlash,
stripTrailingSlash,
Expand Down Expand Up @@ -308,12 +308,16 @@ const createBrowserHistory = (props = {}) => {
};
};

const link = (path, state) =>
shouldReplace(history.location, path, state) ? replace(path, state) : push(path, state);

const history = {
length: globalHistory.length,
action: "POP",
location: initialLocation,
createHref,
push,
link,
replace,
go,
goBack,
Expand Down
6 changes: 5 additions & 1 deletion modules/createHashHistory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import warning from "warning";
import invariant from "invariant";
import { createLocation, locationsAreEqual } from "./LocationUtils";
import { createLocation, locationsAreEqual, shouldReplace } from "./LocationUtils";
import {
addLeadingSlash,
stripLeadingSlash,
Expand Down Expand Up @@ -327,12 +327,16 @@ const createHashHistory = (props = {}) => {
};
};

const link = path =>
shouldReplace(history.location, path) ? replace(path) : push(path);

const history = {
length: globalHistory.length,
action: "POP",
location: initialLocation,
createHref,
push,
link,
replace,
go,
goBack,
Expand Down
6 changes: 5 additions & 1 deletion modules/createMemoryHistory.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import warning from "warning";
import { createPath } from "./PathUtils";
import { createLocation } from "./LocationUtils";
import { createLocation, shouldReplace } from "./LocationUtils";
import createTransitionManager from "./createTransitionManager";

const clamp = (n, lowerBound, upperBound) =>
Expand Down Expand Up @@ -156,6 +156,9 @@ const createMemoryHistory = (props = {}) => {

const listen = listener => transitionManager.appendListener(listener);

const link = (path, state) =>
shouldReplace(window.location, path, state) ? replace(path, state) : push(path, state);

const history = {
length: entries.length,
action: "POP",
Expand All @@ -164,6 +167,7 @@ const createMemoryHistory = (props = {}) => {
entries,
createHref,
push,
link,
replace,
go,
goBack,
Expand Down