Skip to content

Commit

Permalink
wrap DropdownItem itemData usages with useMemo (#114)
Browse files Browse the repository at this point in the history
This commit updates DropdownItem itemData usages to be wrapped inside a useMemo.
Originally, I tried refactoring Dropdown to not use itemData, since itemData feels
a bit like an anti pattern, however this proved to be very difficult.

I also tried wrapping DropdownItem usages inside a parent component, like
FilterSearchDropdownItem, however recursivelyMapChildren does not work
for wrapper components that internally use DropdownItem. It only works
when DropdownItem is specified as a direct child.

So, as a workaround, I wrapped itemData usages inside useMemo by creating
a 2d matrix of item datas, itemDataMatrix, which is then referenced by component
usages.

J=SLAP-2016
TEST=manual

see that autocomplete options appear and can be selected
see that filtersearch results appear and can be selected and applied to the search
resolved all eslint warnings. yay!
  • Loading branch information
oshi97 authored Apr 14, 2022
1 parent 1a3785b commit 897e720
Show file tree
Hide file tree
Showing 3 changed files with 39 additions and 17 deletions.
2 changes: 1 addition & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = {
ignorePatterns: ['lib', 'tests/setup/responses', 'storybook-static', '!.storybook'],
overrides: [
{
files: ['**/*.test.*'],
files: ['**/*.{test,stories}.*'],
rules: {
'react-perf/jsx-no-new-array-as-prop': 'off',
'react-perf/jsx-no-new-function-as-prop': 'off',
Expand Down
43 changes: 28 additions & 15 deletions src/components/FilterSearch.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AutocompleteResult, Filter, FilterSearchResponse, SearchParameterField, useAnswersActions } from '@yext/answers-headless-react';
import { useCallback } from 'react';
import { useCallback, useMemo } from 'react';
import { CompositionMethod, useComposedCssClasses } from '../hooks/useComposedCssClasses';
import { useSynchronizedRequest } from '../hooks/useSynchronizedRequest';
import { executeSearch } from '../utils';
Expand Down Expand Up @@ -84,7 +84,10 @@ export function FilterSearch({
inputValue => answersActions.executeFilterSearch(inputValue ?? '', sectioned, searchParamFields),
(e) => console.error('Error occured executing a filter search request.\n', e)
);
const sections = filterSearchResponse?.sections.filter(section => section.results.length > 0) ?? [];
const sections = useMemo(() => {
return filterSearchResponse?.sections.filter(section => section.results.length > 0) ?? [];
}, [filterSearchResponse?.sections]);

const hasResults = sections.flatMap(s => s.results).length > 0;

const handleSelectDropdown = useCallback((_value, _index, itemData) => {
Expand All @@ -100,6 +103,15 @@ export function FilterSearch({
const handleChangeDropdownInput = useCallback(query => executeFilterSearch(query), [executeFilterSearch]);
const meetsSubmitCritera = useCallback(index => index >= 0, []);

const itemDataMatrix = useMemo(() => {
return sections.map(section => {
return section.results.map(result => ({
filter: result.filter,
displayName: result.value
}));
});
}, [sections]);

function renderDropdownItems() {
return sections.map((section, sectionIndex) => {
return (
Expand All @@ -115,7 +127,7 @@ export function FilterSearch({
key={index}
focusedClassName={cssClasses.focusedOption}
value={result.value}
itemData={{ filter: result.filter, displayName: result.value }}
itemData={itemDataMatrix[sectionIndex][index]}
>
{renderAutocompleteResult(result, cssClasses)}
</DropdownItem>
Expand Down Expand Up @@ -162,18 +174,19 @@ function getScreenReaderText(sections: {
pluralForm: '0 autocomplete options found.',
count: 0
});
if (sections.length > 0) {
const screenReaderPhrases = sections.map(section => {
const optionInfo = section.label
? `${section.results.length} ${section.label}`
: `${section.results.length}`;
return processTranslation({
phrase: `${optionInfo} autocomplete option found.`,
pluralForm: `${optionInfo} autocomplete options found.`,
count: section.results.length
});
});
screenReaderText = screenReaderPhrases.join(' ');
if (sections.length === 0) {
return screenReaderText;
}
const screenReaderPhrases = sections.map(section => {
const optionInfo = section.label
? `${section.results.length} ${section.label}`
: `${section.results.length}`;
return processTranslation({
phrase: `${optionInfo} autocomplete option found.`,
pluralForm: `${optionInfo} autocomplete options found.`,
count: section.results.length
});
});
screenReaderText = screenReaderPhrases.join(' ');
return screenReaderText;
}
11 changes: 10 additions & 1 deletion src/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { useSearchBarAnalytics } from '../hooks/useSearchBarAnalytics';
import { isVerticalLink, VerticalLink } from '../models/verticalLink';
import { executeAutocomplete as executeAutocompleteSearch } from '../utils/search-operations';
import { clearStaticRangeFilters } from '../utils/filterutils';
import { useMemo } from 'react';

const builtInCssClasses: SearchBarCssClasses = {
container: 'h-12 mb-6',
Expand Down Expand Up @@ -312,6 +313,14 @@ export function SearchBar({
));
}

const itemDataMatrix = useMemo(() => {
return autocompleteResponse?.results.map(result => {
return result.verticalKeys?.map(verticalKey => ({
verticalLink: { verticalKey, query: result.value }
})) ?? [];
}) ?? [];
}, [autocompleteResponse?.results]);

function renderQuerySuggestions() {
return autocompleteResponse?.results.map((result, i) => (
<Fragment key={i}>
Expand All @@ -334,7 +343,7 @@ export function SearchBar({
className={cssClasses.optionContainer}
focusedClassName={classNames(cssClasses.optionContainer, cssClasses.focusedOption)}
value={result.value}
itemData={{ verticalLink: { verticalKey, query: result.value } }}
itemData={itemDataMatrix[i][j]}
onClick={handleSubmit}
>
{renderAutocompleteResult(
Expand Down

0 comments on commit 897e720

Please sign in to comment.