I have a working code to have 2 search bars with dropdown suggestions, selectable by both mouse and arrow keys in TypeScript and Fresh/Preact. In there I have to explicitly declare individual hooks for different lists:
const list1 = ['rabbits', 'raccoons', 'reindeer', 'red pandas', 'rhinoceroses', 'river otters', 'rattlesnakes', 'roosters'] as const
const list2 = ['jacaranda', 'jacarta', 'jack-o-lantern orange', 'jackpot', 'jade', 'jade green', 'jade rosin', 'jaffa'];
export default function SearchBar() {
const [resultList1, setResultList1] = useState<null | (typeof list1[number])[]>(null);
const [cursor1, setCursor1] = useState<Cursor>(0);
const [selectedItem1, setSelectedItem1] = useState<null | typeof list1[number]>(null); // State to track selected item for list 1
const [resultList2, setResultList2] = useState<null | (typeof list2[number])[]>(null);
const [cursor2, setCursor2] = useState<null | number>(0); // State to track selected item for list 2
const [selectedItem2, setSelectedItem2] = useState<null | typeof list2[number]>(null); // State to track selected item for list 2
/** activeList is used to determine whether the suggestion list should be popup or not */
const [activeList, setActiveList] = useState<null | '1' | '2'>(null); // State to track active list
And explicitly return individual divs for different lists.
<div
id='search-div-1'
className="search-bar-container"
>
<input
type="text"
placeholder={'Search list 1'}
onInput={(e) => {
setResultList1(list1.filter(item => item.includes((e.target as HTMLTextAreaElement).value)));
}}
onFocus={() => setActiveList('1')}
onKeyDown={(e) => handleKeyDown(e)}
/>
<br />
{resultList1 && activeList === '1' ? SuggestedList() : null}
Cursor: {cursor1}<br />
Selected item: <span id="Item 1">{selectedItem1}</span><br />
</div>
<div
id='search-div-2'
className="search-bar-container"
>
<input
type="text"
placeholder={'Search list 2'}
onInput={(e) => {
setResultList2(list2.filter(item => item.includes((e.target as HTMLTextAreaElement).value)));
}}
onFocus={() => setActiveList('2')}
onKeyDown={(e) => handleKeyDown(e)}
/>
<br />
{resultList2 && activeList === '2' ? SuggestedList() : null}
I wonder if this is a good approach or not. My next step for this is to have the suggested lists disappear when they are unfocused by integrating it with Detect click outside multiple components.
-
3\$\begingroup\$ The question is related to the following meta post: codereview.meta.stackexchange.com/q/10949 \$\endgroup\$Peilonrayz– Peilonrayz ♦2024年03月29日 11:21:45 +00:00Commented Mar 29, 2024 at 11:21
1 Answer 1
You can refactor that component into a main component, and each list will have its own component. Then you can reuse the list component multiple times:
export default function SearchBarSplit() {
/** Active list is used to determine whether the search list should be popup or not */
const [activeList, setActiveList] = useState<ActiveList>(null);
return (
<>
<SearchDiv listName="1" list={list1} activeList={activeList} setActiveList={setActiveList} />
<SearchDiv listName="2" list={list2} activeList={activeList} setActiveList={setActiveList} />
Active list: <strong>{activeList}</strong>
</>
);
}
function SearchDiv({listName, list, activeList, setActiveList}: {listName: '1' | '2', list: List, activeList: ActiveList, setActiveList: StateUpdater<ActiveList>}){
const [searchList, setSearchList] = useState<null | SearchList>(null);
const [cursor, setCursor] = useState<Cursor>(0);
const [selectedItem, setSelectedItem] = useState<null | SelectedItem>(null);
...
Head over to Managing State – React to learn more many ways to manage states.