Skip to content

Commit 7cca9a4

Browse files
committed
fix(numericRefinementList): Correctly apply active class
Fixes #1010 The `shouldComponentUpdate` of `RefinementList` was checking if the `facetValues` from the props where actually changed before issuing a new rendering. It turns out that in `numericRefinementList`, the new `facetValues` was always equal to the already set one. More precisely, the filters seemed to already be set in the props before reaching `shouldComponentUpdate`. Turns out that we were editing a variable in place instead of working on a copy so the internal props where changing without the component really noticing. I updated the `render` method of the `numericRefinementList` to pass a new array instead of working on the same copy over and over again. I refactored the `shouldComponentUpdate` and `_toggleRefinement` methods by splitting in several variables in my quest to find the issue. Thought I might keep it that way. I added an explicit regression test to make sure we are really not touching existing variables. Now the current and next props are really different, and the full render is propagated and the correct classes applied.# Possible types : feat, fix, refactor, chore, style, perf, test, docs
1 parent 6b8e34e commit 7cca9a4

File tree

3 files changed

+37
-8
lines changed

3 files changed

+37
-8
lines changed

src/components/RefinementList/RefinementList.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ class RefinementList extends React.Component {
1717
}
1818

1919
shouldComponentUpdate(nextProps, nextState) {
20-
return nextState !== this.state || !isEqual(this.props.facetValues, nextProps.facetValues);
20+
let isStateDifferent = nextState !== this.state;
21+
let isFacetValuesDifferent = !isEqual(this.props.facetValues, nextProps.facetValues);
22+
let shouldUpdate = isStateDifferent || isFacetValuesDifferent;
23+
return shouldUpdate;
2124
}
2225

2326
refine(facetValueToRefine, isRefined) {

src/widgets/numeric-refinement-list/__tests__/numeric-refinement-list-test.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React from 'react';
44
import expect from 'expect';
55
import sinon from 'sinon';
66
import jsdom from 'jsdom-global';
7+
import cloneDeep from 'lodash/lang/cloneDeep';
78

89
import expectJSX from 'expect-jsx';
910
import numericRefinementList from '../numeric-refinement-list.js';
@@ -183,6 +184,28 @@ describe('numericRefinementList()', () => {
183184
expect(helper.search.calledOnce).toBe(true, 'search called once');
184185
});
185186

187+
it('does not alter the initial options when rendering', () => {
188+
// Note: https://github.com/algolia/instantsearch.js/issues/1010
189+
// Make sure we work on a copy of the initial facetValues when rendering,
190+
// not directly editing it
191+
192+
// Given
193+
let initialOptions = [{start: 0, end: 5, name: '1-5'}];
194+
let initialOptionsClone = cloneDeep(initialOptions);
195+
let testWidget = numericRefinementList({
196+
container,
197+
attributeName: 'price',
198+
options: initialOptions
199+
});
200+
201+
// When
202+
testWidget.render({state, results, createURL});
203+
204+
// Then
205+
expect(initialOptions).toEqual(initialOptionsClone);
206+
});
207+
208+
186209
afterEach(() => {
187210
numericRefinementList.__ResetDependency__('ReactDOM');
188211
numericRefinementList.__ResetDependency__('autoHideContainerHOC');

src/widgets/numeric-refinement-list/numeric-refinement-list.js

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,18 @@ function numericRefinementList({
9393
templates
9494
});
9595

96-
this._toggleRefinement = facetValue => helper
97-
.setState(refine(helper.state, attributeName, options, facetValue))
98-
.search();
96+
this._toggleRefinement = (facetValue) => {
97+
let refinedState = refine(helper.state, attributeName, options, facetValue);
98+
helper.setState(refinedState).search();
99+
};
99100
},
100101
render: function({results, state, createURL}) {
101-
let facetValues = options.map(facetValue => {
102-
facetValue.isRefined = isRefined(state, attributeName, facetValue);
103-
facetValue.attributeName = attributeName;
104-
return facetValue;
102+
let facetValues = options.map((facetValue) => {
103+
return {
104+
...facetValue,
105+
isRefined: isRefined(state, attributeName, facetValue),
106+
attributeName: attributeName
107+
};
105108
});
106109

107110
// Bind createURL to this specific attribute

0 commit comments

Comments
 (0)