Skip to content

Commit 3157abc

Browse files
author
Alexandre Stanislawski
committed
fix(url-sync): Makes url sync more reliable
2 main changes here : - the url-sync internal widget now relies on the helper directly and not on the instantsearch lifecycle anymore. - there is dedicated channel of update for the changes coming from the history (private API for the moment) FIX #730 FIX #729
1 parent 7d1d03a commit 3157abc

File tree

6 files changed

+88
-61
lines changed

6 files changed

+88
-61
lines changed

dev/app.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,7 @@ var search = instantsearch({
55
appId: 'latency',
66
apiKey: '6be0576ff61c053d5f9a3225e2a90f76',
77
indexName: 'instant_search',
8-
urlSync: {
9-
useHash: true
10-
}
8+
urlSync: true
119
});
1210

1311
search.addWidget(
@@ -281,3 +279,4 @@ search.addWidget(
281279
);
282280

283281
search.start();
282+

src/lib/InstantSearch.js

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import forEach from 'lodash/collection/forEach';
55
import merge from 'lodash/object/merge';
66
import union from 'lodash/array/union';
77

8-
let EventEmitter = require('events').EventEmitter;
8+
import {EventEmitter} from 'events';
99

1010
import urlSyncWidget from './url-sync.js';
1111
import version from './version.js';
@@ -20,7 +20,8 @@ function defaultCreateURL() { return '#'; }
2020
* @param {string} [options.numberLocale] The locale used to display numbers. This will be passed
2121
* to Number.prototype.toLocaleString()
2222
* @param {Object} [options.searchParameters] Additional parameters to pass to
23-
* the Algolia API. [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
23+
* the Algolia API.
24+
* [Full documentation](https://community.algolia.com/algoliasearch-helper-js/docs/SearchParameters.html)
2425
* @param {Object|boolean} [options.urlSync] Url synchronization configuration.
2526
* Setting to `true` will synchronize the needed search parameters with the browser url.
2627
* @param {string[]} [options.urlSync.trackedParameters] Parameters that will
@@ -66,7 +67,7 @@ Usage: instantsearch({
6667
helpers: require('./helpers.js')({numberLocale}),
6768
compileOptions: {}
6869
};
69-
this.urlSync = urlSync;
70+
this.urlSync = urlSync === true ? {} : urlSync;
7071
}
7172

7273
/**
@@ -93,8 +94,12 @@ Usage: instantsearch({
9394
if (this.urlSync) {
9495
let syncWidget = urlSyncWidget(this.urlSync);
9596
this._createURL = syncWidget.createURL.bind(syncWidget);
97+
this._onHistoryChange = syncWidget.onHistoryChange.bind(syncWidget);
9698
this.widgets.push(syncWidget);
97-
} else this._createURL = defaultCreateURL;
99+
} else {
100+
this._createURL = defaultCreateURL;
101+
this._onHistoryChange = function() {};
102+
}
98103

99104
this.searchParameters = this.widgets.reduce(enhanceConfiguration, this.searchParameters);
100105

@@ -136,10 +141,10 @@ Usage: instantsearch({
136141
}
137142

138143
_init(state, helper) {
144+
const {_onHistoryChange, templatesConfig} = this;
139145
forEach(this.widgets, function(widget) {
140146
if (widget.init) {
141-
const templatesConfig = this.templatesConfig;
142-
widget.init({state, helper, templatesConfig});
147+
widget.init({state, helper, templatesConfig, onHistoryChange: _onHistoryChange});
143148
}
144149
}, this);
145150
}

src/lib/__tests__/InstantSearch-test.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ describe('InstantSearch lifecycle', () => {
3939
apiKey = 'apiKey';
4040
indexName = 'lifecycle';
4141

42-
searchParameters = {some: 'configuration', values: [-2, -1], index: indexName, another: {config: 'parameter'}};
42+
searchParameters = {
43+
some: 'configuration',
44+
values: [-2, -1],
45+
index: indexName,
46+
another: {config: 'parameter'}
47+
};
4348

4449
InstantSearch.__Rewire__('algoliasearch', algoliasearch);
4550
InstantSearch.__Rewire__('algoliasearchHelper', algoliasearchHelper);
@@ -113,7 +118,12 @@ describe('InstantSearch lifecycle', () => {
113118
.toEqual([
114119
client,
115120
indexName,
116-
{some: 'modified', values: [-2, -1], index: indexName, another: {different: 'parameter', config: 'parameter'}}
121+
{
122+
some: 'modified',
123+
values: [-2, -1],
124+
index: indexName,
125+
another: {different: 'parameter', config: 'parameter'}
126+
}
117127
]);
118128
});
119129

@@ -125,12 +135,11 @@ describe('InstantSearch lifecycle', () => {
125135
expect(widget.init.calledOnce).toBe(true, 'widget.init called once');
126136
expect(widget.init.calledAfter(widget.getConfiguration))
127137
.toBe(true, 'widget.init() was called after widget.getConfiguration()');
128-
expect(widget.init.args[0][0]).
129-
toEqual({
130-
state: helper.state,
131-
helper,
132-
templatesConfig: search.templatesConfig
133-
});
138+
const args = widget.init.args[0][0];
139+
expect(args.state).toBe(helper.state);
140+
expect(args.helper).toBe(helper);
141+
expect(args.templatesConfig).toBe(search.templatesConfig);
142+
expect(args.onHistoryChange).toBe(search._onHistoryChange);
134143
});
135144

136145
it('does not call widget.render', () => {

src/lib/url-sync.js

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import algoliasearchHelper from 'algoliasearch-helper';
22
let AlgoliaSearchHelper = algoliasearchHelper.AlgoliaSearchHelper;
33
let majorVersionNumber = require('../lib/version.js').split('.')[0];
4+
import urlHelper from 'algoliasearch-helper/src/url';
45

56
import isEqual from 'lodash/lang/isEqual';
67
import merge from 'lodash/object/merge';
@@ -104,42 +105,42 @@ class URLSync {
104105
return config;
105106
}
106107

107-
onPopState(helper) {
108-
let qs = this.urlUtils.readUrl();
109-
let partialState = AlgoliaSearchHelper.getConfigurationFromQueryString(qs);
110-
let fullState = merge({}, this.originalConfig, partialState);
108+
init({helper}) {
109+
helper.on('change', (state) => {
110+
this.renderURLFromState(state);
111+
});
112+
this.onHistoryChange(this.onPopState.bind(this, helper));
113+
}
114+
115+
onPopState(helper, fullState) {
111116
// compare with helper.state
112117
let partialHelperState = helper.getState(this.trackedParameters);
113118
let fullHelperState = merge({}, this.originalConfig, partialHelperState);
114119

115120
if (isEqual(fullHelperState, fullState)) return;
116121

117-
helper.setState(fullState).search();
122+
helper.overrideStateWithoutTriggeringChangeEvent(fullState).search();
118123
}
119124

120-
init({helper}) {
121-
this.urlUtils.onpopstate(this.onPopState.bind(this, helper));
122-
}
123-
124-
render({helper}) {
125-
let helperState = helper.getState(this.trackedParameters);
125+
renderURLFromState(state) {
126126
let currentQueryString = this.urlUtils.readUrl();
127-
let urlState = AlgoliaSearchHelper.getConfigurationFromQueryString(currentQueryString);
128-
129-
if (isEqual(helperState, urlState)) return;
130-
131-
// Add instantsearch version to reconciliate old url with newer versions
132127
let foreignConfig = AlgoliaSearchHelper.getForeignConfigurationInQueryString(currentQueryString);
133128
foreignConfig.is_v = majorVersionNumber;
134129

135-
let qs = helper.getStateAsQueryString({filters: this.trackedParameters, moreAttributes: foreignConfig});
130+
let qs = urlHelper.getQueryStringFromState(
131+
state.filter(this.trackedParameters),
132+
{moreAttributes: foreignConfig}
133+
);
134+
136135
if (this.timer() < this.threshold) {
137136
this.urlUtils.replaceState(qs);
138137
} else {
139138
this.urlUtils.pushState(qs);
140139
}
141140
}
142141

142+
// External API's
143+
143144
createURL(state) {
144145
let currentQueryString = this.urlUtils.readUrl();
145146
let filteredState = state.filter(this.trackedParameters);
@@ -149,6 +150,15 @@ class URLSync {
149150

150151
return this.urlUtils.createURL(algoliasearchHelper.url.getQueryStringFromState(filteredState));
151152
}
153+
154+
onHistoryChange(fn) {
155+
this.urlUtils.onpopstate(() => {
156+
let qs = this.urlUtils.readUrl();
157+
let partialState = AlgoliaSearchHelper.getConfigurationFromQueryString(qs);
158+
let fullState = merge({}, this.originalConfig, partialState);
159+
fn(fullState);
160+
});
161+
}
152162
}
153163

154164
/**

0 commit comments

Comments
 (0)