Skip to content

Commit

Permalink
fix(instantsearch): remove searchFunction
Browse files Browse the repository at this point in the history
This option is deprecated and all use cases are possible with onStateChange or other methods too

[FX-3216]

BREAKING CHANGE: replace searchFunction usage with onStateChange
  • Loading branch information
Haroenv committed Dec 31, 2024
1 parent 500c285 commit 625ca9b
Show file tree
Hide file tree
Showing 11 changed files with 4 additions and 313 deletions.
111 changes: 0 additions & 111 deletions packages/instantsearch-core/src/__tests__/RoutingManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,68 +186,6 @@ describe('RoutingManager', () => {
});
});

test('should apply state mapping on differences after searchFunction', async () => {
const searchClient = createSearchClient();

const router = createFakeRouter({
write: jest.fn(),
});

const stateMapping = createFakeStateMapping({
stateToRoute(uiState) {
return Object.keys(uiState).reduce((state, indexId) => {
const indexState = uiState[indexId];

return {
...state,
[indexId]: {
query: indexState.query && indexState.query.toUpperCase(),
},
};
}, {});
},
});

const search = instantsearch({
indexName: 'indexName',
searchFunction: (helper) => {
helper.setQuery('test').search();
},
searchClient,
routing: {
stateMapping,
router,
},
});

search.addWidgets([
createWidget({
getWidgetUiState(uiState, { searchParameters }) {
return {
...uiState,
query: searchParameters.query,
};
},
getWidgetSearchParameters: jest.fn(
(searchParameters) => searchParameters
),
}),
]);

search.start();

await wait(0);
// initialization is done at this point

expect(search.mainIndex.getHelper()!.state.query).toEqual('test');

expect(router.write).toHaveBeenLastCalledWith({
indexName: {
query: 'TEST',
},
});
});

test('should keep the UI state up to date on state changes', async () => {
const searchClient = createSearchClient();
const stateMapping = createFakeStateMapping({});
Expand Down Expand Up @@ -299,55 +237,6 @@ describe('RoutingManager', () => {
expect(router.write).toHaveBeenCalledTimes(1);
});

test('should keep the UI state up to date on first render', async () => {
const searchClient = createSearchClient();
const stateMapping = createFakeStateMapping({});
const router = createFakeRouter({
write: jest.fn(),
});

const search = instantsearch({
indexName: 'indexName',
searchFunction(helper) {
// Force the value of the query
helper.setQuery('Apple iPhone').search();
},
searchClient,
routing: {
router,
stateMapping,
},
});

const fakeSearchBox = connectSearchBox(() => {})({});
const fakeHitsPerPage = connectHitsPerPage(() => {})({
items: [{ default: true, value: 1, label: 'one' }],
});

search.addWidgets([fakeSearchBox, fakeHitsPerPage]);

// Trigger the call to `searchFunction` -> Apple iPhone
search.start();

await wait(0);

expect(router.write).toHaveBeenCalledTimes(1);
expect(router.write).toHaveBeenLastCalledWith({
indexName: {
query: 'Apple iPhone',
},
});

// Trigger change
search.removeWidgets([fakeHitsPerPage]);

await wait(0);

// The UI state hasn't changed so `router.write` wasn't called a second
// time
expect(router.write).toHaveBeenCalledTimes(1);
});

test('should keep the UI state up to date on router.update', async () => {
const searchClient = createSearchClient();
const stateMapping = createFakeStateMapping({});
Expand Down
46 changes: 0 additions & 46 deletions packages/instantsearch-core/src/__tests__/instantsearch.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -824,52 +824,6 @@ describe('start', () => {
).toHaveBeenCalledTimes(1);
});

it('calls the provided `searchFunction` with a single request', async () => {
const searchFunction = jest.fn((helper) =>
helper.setQuery('test').search()
);
const searchClient = createSearchClient();
const search = new InstantSearch({
indexName: 'indexName',
searchFunction,
searchClient,
});

search.addWidgets([virtualSearchBox({})]);

expect(searchFunction).toHaveBeenCalledTimes(0);
expect(searchClient.search).toHaveBeenCalledTimes(0);

search.start();

await wait(0);

expect(searchFunction).toHaveBeenCalledTimes(1);
expect(searchClient.search).toHaveBeenCalledTimes(1);
expect(search.mainIndex.getHelper()!.state.query).toBe('test');
});

it('calls the provided `searchFunction` with multiple requests', () => {
const searchClient = createSearchClient();
const search = new InstantSearch({
indexName: 'indexName',
searchClient,
searchFunction(helper) {
const nextState = helper.state
.addDisjunctiveFacet('brand')
.addDisjunctiveFacetRefinement('brand', 'Apple');

helper.setState(nextState).search();
},
});

search.addWidgets([virtualSearchBox({})]);

expect(() => {
search.start();
}).not.toThrow();
});

it('forwards the `initialUiState` to the main index', () => {
const search = new InstantSearch({
indexName: 'indexName',
Expand Down
39 changes: 0 additions & 39 deletions packages/instantsearch-core/src/instantsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import version from './version';
import { index } from './widgets/index-widget';

import type {
SearchClient,
Widget,
IndexWidget,
UiState,
Expand Down Expand Up @@ -75,7 +74,6 @@ export class InstantSearch<
_initialUiState: TUiState;
_initialResults: InitialResults | null;
_createURL: CreateURL<TUiState>;
_searchFunction?: InstantSearchOptions['searchFunction'];
_mainHelperSearch?: AlgoliaSearchHelper['search'];
_hasSearchWidget: boolean = false;
_hasRecommendWidget: boolean = false;
Expand Down Expand Up @@ -106,7 +104,6 @@ export class InstantSearch<
initialUiState = {} as TUiState,
routing = null,
insights = undefined,
searchFunction,
stalledSearchDelay = 200,
searchClient = null,
onStateChange = null,
Expand Down Expand Up @@ -170,14 +167,6 @@ See ${createDocumentationLink({

this._insights = insights;

if (searchFunction) {
warning(
false,
`The \`searchFunction\` option is deprecated. Use \`onStateChange\` instead.`
);
this._searchFunction = searchFunction;
}

this.sendEventToInsights = noop;

if (routing) {
Expand Down Expand Up @@ -336,34 +325,6 @@ See ${createDocumentationLink({
return mainHelper;
};

if (this._searchFunction) {
// this client isn't used to actually search, but required for the helper
// to not throw errors
const fakeClient = {
search: () => new Promise(noop),
} as any as SearchClient;

this._mainHelperSearch = mainHelper.search.bind(mainHelper);
mainHelper.search = () => {
const mainIndexHelper = this.mainIndex.getHelper()!;
const searchFunctionHelper = algoliasearchHelper(
fakeClient,
mainIndexHelper.state.index,
mainIndexHelper.state
);
searchFunctionHelper.once('search', ({ state }) => {
mainIndexHelper.overrideStateWithoutTriggeringChangeEvent(state);
this._mainHelperSearch!();
});
// Forward state changes from `searchFunctionHelper` to `mainIndexHelper`
searchFunctionHelper.on('change', ({ state }) => {
mainIndexHelper.setState(state);
});
this._searchFunction!(searchFunctionHelper);
return mainHelper;
};
}

// Only the "main" Helper emits the `error` event vs the one for `search`
// and `results` that are also emitted on the derived one.
mainHelper.on('error', (error) => {
Expand Down
8 changes: 0 additions & 8 deletions packages/instantsearch-core/src/types/instantsearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import type { SearchClient } from './algoliasearch';
import type { InsightsProps } from './insights';
import type { RouterProps } from './router';
import type { UiState } from './ui-state';
import type { AlgoliaSearchHelper } from 'algoliasearch-helper';

export type InstantSearchOptions<
TUiState extends UiState = UiState,
Expand Down Expand Up @@ -45,13 +44,6 @@ export type InstantSearchOptions<
* to `Number.prototype.toLocaleString()`
*/
numberLocale?: string;
/**
* A hook that will be called each time a search needs to be done, with the
* helper as a parameter. It's your responsibility to call `helper.search()`.
* This option allows you to avoid doing searches at page load for example.
* @deprecated use onStateChange instead
*/
searchFunction?: (helper: AlgoliaSearchHelper) => void;
/**
* Function called when the state changes.
*
Expand Down
8 changes: 4 additions & 4 deletions packages/instantsearch.js/stories/instantsearch.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import { withHits } from '../.storybook/decorators';

storiesOf('Basics/InstantSearch', module)
.add(
'with searchFunction to prevent search',
'with onStateChange to prevent search',
withHits(() => {}, {
searchFunction: (helper) => {
const query = helper.state.query;
onStateChange({ uiState, setUiState }) {
const query = uiState.instant_search?.query ?? '';

if (query === '') {
return;
}

helper.search();
setUiState(uiState);
},
})
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -775,55 +775,6 @@ describe('InstantSearch', () => {
});
});

test('updates searchFunction on searchFunction prop change', async () => {
const searchClient = createAlgoliaSearchClient({});
const searchFunction1 = jest.fn((helper) => {
helper.search();
});
const searchFunction2 = jest.fn((helper) => {
helper.search();
});

function App({
searchFunction,
}: Pick<InstantSearchProps, 'searchFunction'>) {
return (
<StrictMode>
<InstantSearch
searchClient={searchClient}
indexName="indexName"
searchFunction={searchFunction}
>
<SearchBox />
</InstantSearch>
</StrictMode>
);
}

const { rerender } = render(<App searchFunction={searchFunction1} />);

await waitFor(() => {
expect(searchFunction1).toHaveBeenCalledTimes(1);
});

userEvent.type(screen.getByRole('searchbox'), 'iphone');

await waitFor(() => {
expect(searchFunction1).toHaveBeenCalledTimes(7);
});

rerender(<App searchFunction={searchFunction2} />);

userEvent.type(screen.getByRole('searchbox'), ' case', {
initialSelectionStart: 6,
});

await waitFor(() => {
expect(searchFunction1).toHaveBeenCalledTimes(7);
expect(searchFunction2).toHaveBeenCalledTimes(5);
});
});

test('triggers no search on unmount', async () => {
const searchClient = createAlgoliaSearchClient({});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,14 +163,6 @@ export function useInstantSearchApi<TUiState extends UiState, TRouteState>(
prevPropsRef.current = props;
}

if (prevProps.searchFunction !== props.searchFunction) {
// Updating the `searchFunction` to `undefined` is not supported by
// InstantSearch.js, so it will throw an error.
// This is a fair behavior until we add an update API in InstantSearch.js.
search._searchFunction = props.searchFunction;
prevPropsRef.current = props;
}

if (prevProps.stalledSearchDelay !== props.stalledSearchDelay) {
// The default `stalledSearchDelay` in InstantSearch.js is 200ms.
// We need to reset it when it's undefined to get back to the original value.
Expand Down
5 changes: 0 additions & 5 deletions packages/vue-instantsearch/src/components/InstantSearch.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,6 @@ export default createInstantSearchComponent({
type: Number,
default: undefined,
},
searchFunction: {
type: Function,
default: undefined,
},
onStateChange: {
type: Function,
default: undefined,
Expand Down Expand Up @@ -98,7 +94,6 @@ export default createInstantSearchComponent({
indexName: this.indexName,
routing: this.routing,
stalledSearchDelay: this.stalledSearchDelay,
searchFunction: this.searchFunction,
onStateChange: this.onStateChange,
initialUiState: this.initialUiState,
future: this.future,
Expand Down
Loading

0 comments on commit 625ca9b

Please sign in to comment.