Created
January 25, 2021 21:58
-
-
Save jessebarocio/326ad8b76de270f4f3c65c3dcfc23834 to your computer and use it in GitHub Desktop.
React AutocompleteTextbox component using Bootstrap 4
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Hide the bootstrap dropdown-menu if it has no children */ | |
.dropdown-menu:empty { | |
display: none !important; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { useState, useCallback, useEffect } from 'react'; | |
import './AutocompleteTextbox.css'; | |
// A simple Autocomplete Textbox component. Allows for loading | |
const AutocompleteTextbox = ({ | |
id, // id passed to the underlying input element | |
className, // classes passed to the underlying input element | |
placeholder, // placeholder for the underlying input element | |
loadItems, // function to load items given a search query | |
renderItem, // function to render items in the autocomplete dropdown | |
onItemSelected, // function to trigger when an autocomplete item is clicked | |
loadDelay = 500 // amount of time (in ms) to delay loading items after a user stops typing in the input | |
}) => { | |
const [query, setQuery] = useState(); | |
const [loading, setLoading] = useState(false); | |
const [results, setResults] = useState([]); | |
const loadItemsHandler = useCallback(async () => { | |
if (query && loadItems) { | |
// Clear the list and set loading = true (so spinner is visible) | |
setResults([]); | |
setLoading(true); | |
// Fetch the items. NOTE: the parent component should handling fetch errors. | |
let items = await loadItems(query); | |
// Set the result list | |
setLoading(false); | |
setResults(items); | |
} | |
}, [query, loadItems]); | |
const onItemClickHandler = (event, item) => { | |
// Cancel out the HTML click event | |
event.preventDefault(); | |
// If onItemSelected is set then execute it | |
if (onItemSelected) { | |
onItemSelected(item); | |
} | |
} | |
// This causes loadItemsHandler execution to be delayed by a configured | |
// number of milliseconds after the user finishes typing. This prevents | |
// unnecessary queries/reloads. | |
useEffect(() => { | |
const timeOutId = setTimeout(() => loadItemsHandler(), loadDelay); | |
return () => clearTimeout(timeOutId); | |
}, [loadItemsHandler, loadDelay]); | |
return ( | |
<div className="dropdown"> | |
<input type="text" | |
id={id} | |
className={'dropdown-toggle ' + className} | |
data-toggle="dropdown" | |
placeholder={placeholder} | |
onChange={event => setQuery(event.target.value)} /> | |
<div className="dropdown-menu"> | |
{loading && (<div className="dropdown-item"> | |
<div className="spinner-grow spinner-grow-sm align-middle" role="status"> | |
<span className="sr-only">Loading...</span> | |
</div> Loading... | |
</div>)} | |
{results.map((item, idx) => | |
<button key={idx} className='dropdown-item' onClick={e => onItemClickHandler(e, item)}> | |
{renderItem ? renderItem(item) : item + ''} | |
</button>)} | |
</div> | |
</div> | |
); | |
} | |
export default AutocompleteTextbox; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment