Skip to content

Commit

Permalink
fix(Instantsearch): Update all props on InstantSearch (#1828)
Browse files Browse the repository at this point in the history
* fix(createInstantSearch): new credentials creates a new client. Previously was creating a new client at each render.

* fix(isManager): implement update of client
* feat(SearchParameters): Move InstantSearch config to <Configure/>
Also:
 - fix the indexName property to be able to update it.
 - small refactoring to explicitely define the props of
createInstantSearch
 - update the doc accordingly
  • Loading branch information
bobylito authored Jan 16, 2017
1 parent a6c513e commit 2ed9b49
Show file tree
Hide file tree
Showing 16 changed files with 619 additions and 448 deletions.
2 changes: 2 additions & 0 deletions docgen/src/examples/tourism/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
SearchBox,
Pagination,
Highlight,
Configure,
} from 'react-instantsearch/dom';
import {
connectHits,
Expand All @@ -28,6 +29,7 @@ const App = props =>
createURL={props.createURL.bind(this)}
onSearchStateChange={props.onSearchStateChange.bind(this)}
>
<Configure aroundLatLngViaIP={true}/>
<Header />
<Filters />
<Results />
Expand Down
6 changes: 3 additions & 3 deletions docgen/src/guide/Search_parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ category: guide
navWeight: 57
---

Algolia has a [wide range of parameters](https://www.algolia.com/doc/api-client/javascript/search#search-parameters). If one of the parameter you want to use is not covered by any widget or connector, then you can directly pass it to the `<InstantSearch>` component.
Algolia has a [wide range of parameters](https://www.algolia.com/doc/api-client/javascript/search#search-parameters). If one of the parameters you want to use is not covered by any widget or connector, then you can use the `<Configure>` widget.

Here's an example configuring the [distinct parameter](https://www.algolia.com/doc/api-client/javascript/parameters#distinct):

Expand All @@ -15,8 +15,8 @@ Here's an example configuring the [distinct parameter](https://www.algolia.com/d
appId="appId"
apiKey="apiKey"
indexName="indexName"
searchParameters={{distinct: 1}}
>
<Configure distinct={1}/>
// widgets
</InstantSearch>
```
Expand All @@ -33,4 +33,4 @@ the [`<HitsPerPage>` widget](widgets/HitsPerPage.html).
<div class="guide-nav-right">
Next: <a href="guide/Search%20state.html">Search state →</a>
</div>
</div>
</div>
1 change: 1 addition & 0 deletions packages/react-instantsearch/connectors.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export {default as connectConfigure} from './src/connectors/connectConfigure.js';
export {default as connectCurrentRefinements} from './src/connectors/connectCurrentRefinements.js';
export {default as connectHierarchicalMenu} from './src/connectors/connectHierarchicalMenu.js';
export {default as connectHighlight} from './src/connectors/connectHighlight.js';
Expand Down
1 change: 1 addition & 0 deletions packages/react-instantsearch/dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const InstantSearch = createInstantSearch(algoliasearch, {
props: {className: 'ais-InstantSearch__root'},
});
export {InstantSearch};
export {default as Configure} from './src/widgets/Configure.js';
export {default as CurrentRefinements} from './src/widgets/CurrentRefinements.js';
export {default as HierarchicalMenu} from './src/widgets/HierarchicalMenu.js';
export {default as Highlight} from './src/widgets/Highlight.js';
Expand Down
1 change: 1 addition & 0 deletions packages/react-instantsearch/native.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ const InstantSearch = createInstantSearch(algoliasearch, {
Root: View,
});
export {InstantSearch};
export {default as Configure} from './src/widgets/Configure.js';
1 change: 1 addition & 0 deletions packages/react-instantsearch/src/components/Configure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default () => null;
11 changes: 11 additions & 0 deletions packages/react-instantsearch/src/connectors/connectConfigure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import createConnector from '../core/createConnector.js';
import omit from 'lodash/omit';

export default createConnector({
displayName: 'AlgoliaConfigure',
getProvidedProps() { return {}; },
getSearchParameters(searchParameters, props) {
const configuration = omit(props, 'children');
return searchParameters.setQueryParameters(configuration);
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* eslint-env jest, jasmine */
import {SearchParameters} from 'algoliasearch-helper';

import connect from './connectConfigure.js';
jest.mock('../core/createConnector');

const {getSearchParameters} = connect;

describe('connectConfigure', () => {
it('it propagates the props to the SearchParameters without children', () => {
const searchParameters = getSearchParameters(
new SearchParameters(),
{distinct: 1, whatever: 'please', children: 'whatever'}
);
expect(searchParameters.getQueryParameter('distinct')).toEqual(1);
expect(searchParameters.getQueryParameter('whatever')).toEqual('please');
expect(searchParameters.getQueryParameter.bind(searchParameters, 'children')).toThrow();
});
});
7 changes: 6 additions & 1 deletion packages/react-instantsearch/src/core/InstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function validateNextProps(props, nextProps) {
* @propType {string} appId - The Algolia application id.
* @propType {string} apiKey - Your Algolia Search-Only API key.
* @propType {string} indexName - The index in which to search.
* @propType {object} [searchParameters] - Object containing query parameters to be sent to Algolia. It will be overriden by the search parameters resolved via the widgets. Typical use case: setting the distinct setting is done by providing an object like: `{distinct: 1}`. For more information about the kind of object that can be provided on the [official API documentation](https://www.algolia.com/doc/rest-api/search#full-text-search-parameters). Read the [search parameters guide](guide/Search_parameters.html).
* @propType {object} [searchParameters] - This method of providing parameters to Algolia is now deprecated. Please use [`<configure/>`](widgets/Configure.html). Object containing query parameters to be sent to Algolia. It will be overriden by the search parameters resolved via the widgets. Typical use case: setting the distinct setting is done by providing an object like: `{distinct: 1}`. For more information about the kind of object that can be provided on the [official API documentation](https://www.algolia.com/doc/rest-api/search#full-text-search-parameters). Read the [search parameters guide](guide/Search_parameters.html).
* @propType {func} onSearchStateChange - See [URL Routing](guide/Routing.html).
* @propType {object} searchState - See [URL Routing](guide/Routing.html).
* @propType {func} createURL - See [URL Routing](guide/Routing.html).
Expand Down Expand Up @@ -61,6 +61,11 @@ class InstantSearch extends Component {

componentWillReceiveProps(nextProps) {
validateNextProps(this.props, nextProps);

if (this.props.indexName !== nextProps.indexName) {
this.aisManager.updateIndex(nextProps.indexName);
}

if (this.isControlled) {
this.aisManager.onExternalStateUpdate(nextProps.searchState);
}
Expand Down
25 changes: 22 additions & 3 deletions packages/react-instantsearch/src/core/createInstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,34 @@ export default function createInstantSearch(defaultAlgoliaClient, root) {
algoliaClient: PropTypes.object,
appId: PropTypes.string,
apiKey: PropTypes.string,
children: React.PropTypes.oneOfType([
React.PropTypes.arrayOf(React.PropTypes.node),
React.PropTypes.node,
]),
indexName: PropTypes.string.isRequired,
};

constructor(props) {
super();
this.client = props.algoliaClient || defaultAlgoliaClient(props.appId, props.apiKey);
}

componentWillReceiveProps(nextProps) {
const props = this.props;
if (nextProps.algoliaClient) {
this.client = nextProps.algoliaClient;
} else if (props.appId !== nextProps.appId || props.apiKey !== nextProps.apiKey) {
this.client = defaultAlgoliaClient(nextProps.appId, nextProps.apiKey);
}
}

render() {
const client = this.props.algoliaClient || defaultAlgoliaClient(this.props.appId, this.props.apiKey);
return (
<InstantSearch
{...this.props}

This comment has been minimized.

Copy link
@zackify

zackify Jan 17, 2017

Aha, is this the problem with #1849 ? @vvo

This comment has been minimized.

Copy link
@vvo

vvo Jan 18, 2017

Contributor

Yes, good find, gg 🥇

indexName={this.props.indexName}
root={root}
algoliaClient={client}
algoliaClient={this.client}
children={this.props.children}
/>
);
}
Expand Down
100 changes: 100 additions & 0 deletions packages/react-instantsearch/src/core/createInstantSearch.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/* eslint-env jest, jasmine */
/* eslint-disable no-console */
import React from 'react';
import {mount} from 'enzyme';

import algoliaClient from 'algoliasearch';

import createInstantSearch from './createInstantSearch';

describe('createInstantSearch', () => {
it('Instanciate the client when no algoliaClient provided', () => {
const apiKey = '332335235235325';
const appId = 'myApp';

const algoliaClientFactory = jest.fn(algoliaClient);
const InstantSearch = createInstantSearch(
algoliaClientFactory, {
Root: 'div',
props: {className: 'ais-InstantSearch__root'},
});

const wrapper = mount(<InstantSearch appId={apiKey} apiKey={appId} indexName=""/>);

const otherApiKey = '50943204984230498';
const otherAppId = 'otherApp';
wrapper.setProps({
apiKey: otherApiKey,
appId: otherAppId,
});

wrapper.setProps({
apiKey: otherApiKey,
appId: otherAppId,
});

expect(algoliaClientFactory).toHaveBeenCalledTimes(2);
});

it('Never call the client factory when algoliaClient is provided', () => {
const algoliaClientFactory = jest.fn(algoliaClient);
const InstantSearch = createInstantSearch(
algoliaClientFactory, {
Root: 'div',
props: {className: 'ais-InstantSearch__root'},
});

const fakeClient = {
addAlgoliaAgent: () => {},
search: () => {},
};

const wrapper = mount(<InstantSearch algoliaClient={fakeClient} indexName=""/>);

wrapper.setProps({
algoliaClient: {
search: () => {},
},
});

expect(algoliaClientFactory).toHaveBeenCalledTimes(0);
});

it('Can switch from provided to non-provided algoliaClient, and instanciate accordingly', () => {
const apiKey = '332335235235325';
const appId = 'myApp';

const algoliaClientFactory = jest.fn(algoliaClient);
const InstantSearch = createInstantSearch(
algoliaClientFactory, {
Root: 'div',
props: {className: 'ais-InstantSearch__root'},
});

const wrapper = mount(<InstantSearch appId={apiKey} apiKey={appId} indexName=""/>);

const otherApiKey = '50943204984230498';
const otherAppId = 'otherApp';
wrapper.setProps({
apiKey: otherApiKey,
appId: otherAppId,
});

wrapper.setProps({
algoliaClient: {
search: () => {},
addAlgoliaAgent: () => {},
},
apiKey: undefined,
appId: undefined,
});

wrapper.setProps({
algoliaClient: undefined,
apiKey: otherApiKey,
appId: otherAppId,
});

expect(algoliaClientFactory).toHaveBeenCalledTimes(3);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default function createInstantSearchManager({
helper.on('result', handleSearchSuccess);
helper.on('error', handleSearchError);

const initialSearchParameters = helper.state;
let initialSearchParameters = helper.state;

const widgetsManager = createWidgetsManager(onWidgetsUpdate);

Expand All @@ -41,6 +41,11 @@ export default function createInstantSearchManager({
searching: false,
});

function updateClient(client) {
helper.setClient(client);
search();
}

function getMetadata(state) {
return widgetsManager.getWidgets()
.filter(widget => Boolean(widget.getMetadata))
Expand Down Expand Up @@ -152,6 +157,11 @@ export default function createInstantSearchManager({
});
}

function updateIndex(newIndex) {
initialSearchParameters = initialSearchParameters.setIndex(newIndex);
search();
}

function getWidgetsIds() {
return store.getState().metadata.reduce((res, meta) =>
typeof meta.id !== 'undefined' ? res.concat(meta.id) : res
Expand All @@ -165,5 +175,7 @@ export default function createInstantSearchManager({
onExternalStateUpdate,
transitionState,
onSearchForFacetValues,
updateClient,
updateIndex,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ jest.mock('algoliasearch-helper/src/algoliasearch.helper.js', () => {
});

const client = algoliaClient('latency', '249078a3d4337a8231f1665ec5a44966');
client.addAlgoliaAgent = () => {};
client.search = jest.fn((queries, cb) => {
if (cb) {
setImmediate(() => {
Expand Down
Loading

0 comments on commit 2ed9b49

Please sign in to comment.