Skip to content

Commit

Permalink
feat: add time travelling support (#15)
Browse files Browse the repository at this point in the history
Now listen to state changes in mobx-state-tree and do history.replace with the new history location state. This allows the location to also update when time travelling using mobx-state-tree.
  • Loading branch information
freund17 authored and alisd23 committed Dec 16, 2018
1 parent b3a5518 commit 3dbcbf9
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 45 deletions.
8 changes: 4 additions & 4 deletions __test__/store.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,24 +22,24 @@ describe('store', () => {
expect(mockHistory.push.mock.calls[0]).toEqual(['push', 'args']);
});

it('can call replace() and proxy arguments through', () => {
it('can call replace() and proxy argument through', () => {
routerModel.replace('replace', 'args');
expect(mockHistory.replace.mock.calls.length).toBe(1);
expect(mockHistory.replace.mock.calls[0]).toEqual(['replace', 'args']);
});

it('can call go() and proxy arguments through', () => {
it('can call go() and proxy argument through', () => {
routerModel.go(-1);
expect(mockHistory.go.mock.calls.length).toBe(1);
expect(mockHistory.go.mock.calls[0]).toEqual([-1]);
});

it('can call goBack() and proxy arguments through', () => {
it('can call goBack()', () => {
routerModel.goBack();
expect(mockHistory.goBack.mock.calls.length).toBe(1);
});

it('can call goForward() and proxy arguments through', () => {
it('can call goForward()', () => {
routerModel.goForward();
expect(mockHistory.goForward.mock.calls.length).toBe(1);
});
Expand Down
56 changes: 55 additions & 1 deletion __test__/sync.spec.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import createMemoryHistory from 'history/createMemoryHistory';
import { applySnapshot } from 'mobx-state-tree';
import { RouterModel, syncHistoryWithStore } from '../index';

let history, memoryHistory, routerModel;
Expand Down Expand Up @@ -36,7 +37,7 @@ beforeEach(() => {
});

describe('syncing', () => {
it('syncs store with history', () => {
it('syncs history to store', () => {
expect(routerModel.action).toBe('POP');
expect(routerModel.location).toEqualLocation({
pathname: '/'
Expand Down Expand Up @@ -70,6 +71,59 @@ describe('syncing', () => {
});
});

it('syncs store to history', async () => {
// Note:
// history.action will always be "replace",
// because IMO, time-travelling shouldn't alter the history.

let location = {
hash: '',
pathname: '/',
search: '',
state: undefined
};
expect(history.action).toBe('POP');
expect(history.location).toEqualLocation(location);

location = { ...location, pathname: '/url-1' };
applySnapshot(routerModel, {
action: 'PUSH',
location
});
expect(history.action).toBe('REPLACE');
expect(history.location).toEqualLocation(location);

location = { ...location, pathname: '/' };
applySnapshot(routerModel, {
action: 'POP',
location
});
expect(history.action).toBe('REPLACE');
expect(history.location).toEqualLocation(location);

location = { ...location, pathname: '/url-1' };
applySnapshot(routerModel, {
action: 'POP',
location
});
expect(history.action).toBe('REPLACE');
expect(history.location).toEqualLocation(location);

location = {
...location,
hash: '#mango',
pathname: '/url-2',
query: { animal: 'fish' },
search: '?animal=fish'
};
applySnapshot(routerModel, {
action: 'REPLACE',
location
});
expect(history.action).toBe('REPLACE');
expect(history.location).toEqualLocation(location);
});

it('provdides a way to unsubscribe the router store from history', () => {
history.push('/');
expect(routerModel.location).toEqualLocation({
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"url": "https://github.com/alisd23/mst-react-router.git"
},
"keywords": [
"react",
"history",
"react-router",
"routing",
"router",
Expand Down Expand Up @@ -57,6 +57,7 @@
"eslint-plugin-promise": "^2.0.1",
"eslint-plugin-react": "^6.3.0",
"eslint-plugin-standard": "^2.0.0",
"history": "^4.7.2",
"jest": "^19.0.0",
"mobx": "^4.3.1",
"mobx-state-tree": "^3.8.0",
Expand All @@ -69,8 +70,9 @@
"webpack-cli": "^2.0.10"
},
"peerDependencies": {
"mobx": "^4.3.1",
"mobx-state-tree": "^3.8.0",
"react-router": "4.x"
"history": "^4.7.2"
},
"dependencies": {}
}
1 change: 0 additions & 1 deletion src/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export const RouterModel = types
},
_setHistory(initialHistory) {
history = initialHistory;
routerModel.location = { ...history.location };
},
push() {
history.push(...arguments);
Expand Down
34 changes: 30 additions & 4 deletions src/sync.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,45 @@
import { reaction } from 'mobx';

/**
* Sync the history object with the given mst router store
* @param {object} history - 'History' instance to subscribe to
* @param {object} history - 'History' instance to subscribe and sync to
* @param {object} store - Router store instance to sync with the history changes
*/
export const syncHistoryWithStore = (history, store) => {
store._setHistory(history);

function isLocationEqual(locationA, locationB) {
return (
locationA &&
locationB &&
locationA.key &&
locationB.key &&
locationA.key === locationB.key
);
}

// Handle update from history object
const handleLocationChange = location => {
store._updateLocation({ ...location });
if (!isLocationEqual(store.location, location)) {
store._updateLocation({ ...location });
}
};
const unsubscribeFromHistory = history.listen(handleLocationChange);
handleLocationChange(history.location);
const unsubscribeFromStoreLocation = reaction(
() => store.location,
location => {
if (!isLocationEqual(history.location, location)) {
history.replace({ ...location });
}
}
);

history.unsubscribe = unsubscribeFromHistory;
history.unsubscribe = () => {
unsubscribeFromHistory();
unsubscribeFromStoreLocation();
};

handleLocationChange(history.location);

return history;
};
10 changes: 9 additions & 1 deletion webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,17 @@ module.exports = {
umdNamedDefine: true
},
externals: [
{
'mobx': {
root: 'mobx',
commonjs2: 'mobx',
commonjs: 'mobx',
amd: 'mobx'
}
},
{
'mobx-state-tree': {
root: 'MobxStateTree',
root: 'mobxStateTree',
commonjs2: 'mobx-state-tree',
commonjs: 'mobx-state-tree',
amd: 'mobx-state-tree'
Expand Down
38 changes: 6 additions & 32 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2128,7 +2128,7 @@ debug@^4.0.0:
dependencies:
ms "^2.1.1"

debuglog@*, debuglog@^1.0.1:
debuglog@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492"

Expand Down Expand Up @@ -3559,7 +3559,7 @@ import-local@^1.0.0:
pkg-dir "^2.0.0"
resolve-cwd "^2.0.0"

imurmurhash@*, imurmurhash@^0.1.4:
imurmurhash@^0.1.4:
version "0.1.4"
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"

Expand Down Expand Up @@ -4693,39 +4693,17 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"

lodash._baseindexof@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c"

lodash._baseuniq@~4.6.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8"
dependencies:
lodash._createset "~4.0.0"
lodash._root "~3.0.0"

lodash._bindcallback@*:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e"

lodash._cacheindexof@*:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92"

lodash._createcache@*:
version "3.1.2"
resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093"
dependencies:
lodash._getnative "^3.0.0"

lodash._createset@~4.0.0:
version "4.0.3"
resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26"

lodash._getnative@*, lodash._getnative@^3.0.0:
version "3.9.1"
resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5"

lodash._root@~3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/lodash._root/-/lodash._root-3.0.1.tgz#fba1c4524c19ee9a5f8136b4609f017cf4ded692"
Expand Down Expand Up @@ -4770,10 +4748,6 @@ lodash.pickby@^4.0.0:
version "4.6.0"
resolved "https://registry.yarnpkg.com/lodash.pickby/-/lodash.pickby-4.6.0.tgz#7dea21d8c18d7703a27c704c15d3b84a67e33aff"

lodash.restparam@*:
version "3.6.1"
resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805"

lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
Expand Down Expand Up @@ -5181,9 +5155,9 @@ mixin-deep@^1.2.0:
dependencies:
minimist "0.0.8"

mobx-state-tree@^3.0.0:
version "3.8.1"
resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-3.8.1.tgz#c493f4006d8f24d73d0be98b9be5fc2753142a77"
mobx-state-tree@^3.8.0:
version "3.9.0"
resolved "https://registry.yarnpkg.com/mobx-state-tree/-/mobx-state-tree-3.9.0.tgz#78fc35fc0b64e6d38335a4dcb2b234f9c7e589e7"

mobx@^4.3.1:
version "4.6.0"
Expand Down Expand Up @@ -6506,7 +6480,7 @@ readable-stream@~1.1.10:
isarray "0.0.1"
string_decoder "~0.10.x"

readdir-scoped-modules@*, readdir-scoped-modules@^1.0.0:
readdir-scoped-modules@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.0.2.tgz#9fafa37d286be5d92cbaebdee030dc9b5f406747"
dependencies:
Expand Down

0 comments on commit 3dbcbf9

Please sign in to comment.