-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Bolt: optimize SearchInterface with useCallback for event handlers #336
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: staging
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,5 @@ | ||
| import { motion } from 'framer-motion' | ||
| import React, { useState } from 'react' | ||
| import React, { useState, useCallback } from 'react' | ||
|
|
||
| import { researchAPI, type BookMetadata } from '@/lib/api/research' | ||
|
|
||
|
|
@@ -30,62 +30,72 @@ export default function SearchInterface() { | |
| // Export State | ||
| const [showExport, setShowExport] = useState(false) | ||
|
|
||
| const executeSearch = async ( | ||
| currentQuery: string, | ||
| currentSources: string[], | ||
| currentFilters: SearchFiltersState, | ||
| ) => { | ||
| if (!currentQuery.trim()) return | ||
|
|
||
| setLoading(true) | ||
| setHasSearched(true) | ||
| setError(null) | ||
| setResults([]) | ||
|
|
||
| try { | ||
| // Track search event | ||
| void researchAPI.trackEvent('search_literature', { | ||
| query: currentQuery, | ||
| sources: currentSources, | ||
| filter_count: | ||
| (currentFilters.topics.length || 0) + | ||
| (currentFilters.yearFrom ? 1 : 0) + | ||
| (currentFilters.yearTo ? 1 : 0), | ||
| }) | ||
|
|
||
| const data = await researchAPI.searchLiterature({ | ||
| q: currentQuery, | ||
| limit: 12, | ||
| sources: currentSources.includes('all') ? undefined : currentSources, | ||
| year_from: currentFilters.yearFrom, | ||
| year_to: currentFilters.yearTo, | ||
| min_relevance: currentFilters.minRelevance, | ||
| topics: currentFilters.topics, | ||
| sort_by: currentFilters.sortBy, | ||
| }) | ||
|
|
||
| setResults(data.results) | ||
| } catch (err: any) { | ||
| console.error('Search error:', err) | ||
| setError(err.message || 'Failed to fetch results.') | ||
| } finally { | ||
| setLoading(false) | ||
| } | ||
| } | ||
|
|
||
| const handleSearch = (e: React.FormEvent) => { | ||
| e.preventDefault() | ||
| void executeSearch(query, selectedSources, filters) | ||
| } | ||
|
|
||
| const handleFilterChange = (newFilters: SearchFiltersState) => { | ||
| setFilters(newFilters) | ||
| setShowFilters(false) | ||
| // Auto-refresh search if we already have a query | ||
| if (query.trim()) { | ||
| void executeSearch(query, selectedSources, newFilters) | ||
| } | ||
| } | ||
| // ⚡ Bolt: wrap executeSearch and related event handlers in useCallback to prevent unnecessary re-creations and re-renders of child components | ||
| const executeSearch = useCallback( | ||
| async ( | ||
| currentQuery: string, | ||
| currentSources: string[], | ||
| currentFilters: SearchFiltersState, | ||
| ) => { | ||
| if (!currentQuery.trim()) return | ||
|
|
||
| setLoading(true) | ||
| setHasSearched(true) | ||
| setError(null) | ||
| setResults([]) | ||
|
|
||
| try { | ||
| // Track search event | ||
| void researchAPI.trackEvent('search_literature', { | ||
| query: currentQuery, | ||
| sources: currentSources, | ||
| filter_count: | ||
| (currentFilters.topics.length || 0) + | ||
| (currentFilters.yearFrom ? 1 : 0) + | ||
| (currentFilters.yearTo ? 1 : 0), | ||
| }) | ||
|
|
||
| const data = await researchAPI.searchLiterature({ | ||
| q: currentQuery, | ||
| limit: 12, | ||
| sources: currentSources.includes('all') ? undefined : currentSources, | ||
| year_from: currentFilters.yearFrom, | ||
| year_to: currentFilters.yearTo, | ||
| min_relevance: currentFilters.minRelevance, | ||
| topics: currentFilters.topics, | ||
| sort_by: currentFilters.sortBy, | ||
| }) | ||
|
|
||
| setResults(data.results) | ||
| } catch (err: any) { | ||
| console.error('Search error:', err) | ||
| setError(err.message || 'Failed to fetch results.') | ||
| } finally { | ||
|
Comment on lines
+70
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The SuggestionUse } catch (err: unknown) {
console.error('Search error:', err)
const message = err instanceof Error ? err.message : 'Failed to fetch results.'
setError(message)
} finally {
setLoading(false)
}Reply with "@CharlieHelps yes please" if you’d like me to add a commit with this suggestion. |
||
| setLoading(false) | ||
| } | ||
| }, | ||
| [], | ||
| ) | ||
|
|
||
| const handleSearch = useCallback( | ||
| (e: React.FormEvent) => { | ||
| e.preventDefault() | ||
| void executeSearch(query, selectedSources, filters) | ||
| }, | ||
| [executeSearch, query, selectedSources, filters], | ||
| ) | ||
|
|
||
| const handleFilterChange = useCallback( | ||
| (newFilters: SearchFiltersState) => { | ||
| setFilters(newFilters) | ||
| setShowFilters(false) | ||
| // Auto-refresh search if we already have a query | ||
| if (query.trim()) { | ||
| void executeSearch(query, selectedSources, newFilters) | ||
| } | ||
| }, | ||
| [executeSearch, query, selectedSources], | ||
| ) | ||
|
Comment on lines
+88
to
+98
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
SuggestionMake the callback stable by not capturing const queryRef = useRef(query)
useEffect(() => {
queryRef.current = query
}, [query])
const sourcesRef = useRef(selectedSources)
useEffect(() => {
sourcesRef.current = selectedSources
}, [selectedSources])
const handleFilterChange = useCallback(
(newFilters: SearchFiltersState) => {
setFilters(newFilters)
setShowFilters(false)
if (queryRef.current.trim()) {
void executeSearch(queryRef.current, sourcesRef.current, newFilters)
}
},
[executeSearch],
)Reply with "@CharlieHelps yes please" if you’d like me to add a commit with this suggestion. |
||
|
|
||
| return ( | ||
| <motion.div className='mx-auto w-full max-w-6xl p-4'> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These assertions are redundant and (as written) weaker than the previous
jest-domversions:getByText/getByLabelTextalready throw if not found, soexpect(...).not.toBeNull()adds no coverage.toHaveAttribute/toBeInTheDocumentreduces readability and intent.If
jest-dommatchers were removed to “make the test pass”, that’s treating the symptom—better to fix the test setup so you can keep expressive assertions.Suggestion
Either remove the redundant expectations entirely, or restore the stronger
jest-dommatchers by ensuring@testing-library/jest-dom/vitestis loaded in your Vitest setup.Option A (remove redundancy):
Reply with "@CharlieHelps yes please" if you’d like me to add a commit with this suggestion.