2
\$\begingroup\$

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}

Full code here.

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.

\$\endgroup\$
1

1 Answer 1

1
\$\begingroup\$

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); 
...

Full code.

Head over to Managing State – React to learn more many ways to manage states.

\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.