mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-22 16:36:47 +08:00
### What problem does this PR solve? Fix: The dataset on the search page is not displaying the required field error message. ### Type of change - [x] Bug Fix (non-breaking change which fixes an issue)
3.7 KiB
3.7 KiB
perf-select-transform: Use Select to Transform and Filter Data
Priority: LOW
Explanation
The select option transforms query data before it reaches your component. Use it for filtering, sorting, or deriving data. Benefits include memoization (re-runs only when data changes) and reduced component re-renders.
Bad Example
// Transforming in component - runs on every render
function CompletedTodos() {
const { data: todos } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
})
// This filtering runs on every render
const completedTodos = todos?.filter(todo => todo.completed) ?? []
const sortedTodos = [...completedTodos].sort((a, b) =>
new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime()
)
return <TodoList todos={sortedTodos} />
}
Good Example
// Using select - runs only when data changes
function CompletedTodos() {
const { data: completedTodos } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select: (todos) =>
todos
.filter(todo => todo.completed)
.sort((a, b) =>
new Date(b.completedAt).getTime() - new Date(a.completedAt).getTime()
),
})
return <TodoList todos={completedTodos ?? []} />
}
Good Example: Selecting Specific Fields
// Derive computed values
function TodoStats() {
const { data: stats } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select: (todos) => ({
total: todos.length,
completed: todos.filter(t => t.completed).length,
pending: todos.filter(t => !t.completed).length,
completionRate: todos.length
? (todos.filter(t => t.completed).length / todos.length) * 100
: 0,
}),
})
return (
<div>
<span>{stats?.completed} / {stats?.total} completed</span>
<span>({stats?.completionRate.toFixed(1)}%)</span>
</div>
)
}
Good Example: Stable Select with useCallback
// When select depends on external values, stabilize with useCallback
function FilteredTodos({ status }: { status: 'all' | 'active' | 'completed' }) {
const selectTodos = useCallback(
(todos: Todo[]) => {
switch (status) {
case 'active':
return todos.filter(t => !t.completed)
case 'completed':
return todos.filter(t => t.completed)
default:
return todos
}
},
[status]
)
const { data: filteredTodos } = useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select: selectTodos,
})
return <TodoList todos={filteredTodos ?? []} />
}
Good Example: Picking Single Item from List
// Select single item from cached list
function useTodoById(id: number) {
return useQuery({
queryKey: ['todos'],
queryFn: fetchTodos,
select: (todos) => todos.find(todo => todo.id === id),
})
}
// Usage - shares cache with list query
function TodoDetail({ id }: { id: number }) {
const { data: todo } = useTodoById(id)
if (!todo) return <div>Todo not found</div>
return <div>{todo.title}</div>
}
When to Use Select
| Scenario | Use Select? |
|---|---|
| Filtering list data | Yes |
| Sorting data | Yes |
| Computing derived values | Yes |
| Picking single item from list | Yes |
| Heavy transformations | Yes (memoized) |
| Simple data pass-through | No |
| Transformation needs external state | Yes, with useCallback |
Context
selectleverages structural sharing - only re-runs when data actually changes- Original query data stays cached; transformation applies to consumer
- Multiple components can use different
selecton the same query - Avoid unstable function references - use
useCallbackwhen needed - For complex transformations, consider useMemo in component instead if readability suffers