11import { useState , useEffect , useRef } from 'react' ;
22import { debounce } from 'lodash' ;
3- import { ArchbaseDataSource , DataSourceEvent , DataSourceEventNames } from 'components/datasource' ;
3+ import { ArchbaseDataSource , DataSourceEvent , DataSourceEventNames , DataSourceOptions } from 'components/datasource' ;
44
55/**
6- * Hook para estabilizar o comportamento da Grid e evitar perda de foco em inputs
7- * ao interagir com a Grid .
6+ * Hook para estabilizar o comportamento da Grid e evitar chamadas duplicadas à busca,
7+ * além de preservar o foco em inputs durante interações .
88 */
99export function useArchbaseDataGridStableRendering < T extends object , ID > ( {
1010 dataSource,
@@ -18,9 +18,16 @@ export function useArchbaseDataGridStableRendering<T extends object, ID>({
1818 const [ totalRecords , setTotalRecords ] = useState < number > ( ( ) => dataSource . getGrandTotalRecords ( ) ) ;
1919 const [ isLoading , setIsLoading ] = useState < boolean > ( false ) ;
2020
21- // Ref para controlar operações em andamento e evitar loops
21+ // Refs para controle de estado
2222 const blockUpdates = useRef < boolean > ( false ) ;
2323 const pendingUpdate = useRef < boolean > ( false ) ;
24+ const refreshInProgress = useRef < boolean > ( false ) ;
25+ const refreshTimestampRef = useRef < number > ( 0 ) ;
26+ const lastRefreshRequestRef = useRef < number > ( 0 ) ;
27+
28+ // Um Map para rastrear quais eventos foram processados recentemente
29+ // Chave: tipo de evento, Valor: timestamp
30+ const processedEvents = useRef < Map < DataSourceEventNames , number > > ( new Map ( ) ) ;
2431
2532 // Função debounce para atualizar os dados de forma eficiente
2633 const debouncedUpdateData = useRef (
@@ -30,49 +37,98 @@ export function useArchbaseDataGridStableRendering<T extends object, ID>({
3037 return ;
3138 }
3239
40+ // Verificar se uma atualização já foi feita recentemente
41+ const now = Date . now ( ) ;
42+ if ( now - refreshTimestampRef . current < debounceTime ) {
43+ return ;
44+ }
45+
3346 try {
34- // Atualizar os dados
3547 setRows ( dataSource . browseRecords ( ) ) ;
3648 setTotalRecords ( dataSource . getGrandTotalRecords ( ) ) ;
3749 setIsLoading ( false ) ;
50+ refreshTimestampRef . current = now ;
51+ refreshInProgress . current = false ;
3852 } catch ( error ) {
3953 console . error ( 'Erro ao atualizar dados:' , error ) ;
4054 setIsLoading ( false ) ;
55+ refreshInProgress . current = false ;
4156 }
4257 } , debounceTime )
4358 ) . current ;
4459
60+ // Função para forçar atualização dos dados com proteção contra chamadas duplicadas
61+ const refreshData = ( ) => {
62+ const now = Date . now ( ) ;
63+
64+ // Verificar se uma requisição de refresh já foi feita recentemente
65+ if ( now - lastRefreshRequestRef . current < debounceTime || refreshInProgress . current ) {
66+ console . log ( 'Ignorando refresh duplicado' ) ;
67+ return ;
68+ }
69+
70+ lastRefreshRequestRef . current = now ;
71+ refreshInProgress . current = true ;
72+ setIsLoading ( true ) ;
73+
74+ // Usar uma referência para rastrear este refresh especificamente
75+ const refreshId = now ;
76+ const eventKey = Symbol ( 'refreshEvent' ) ;
77+
78+ // Armazenar o ID do refresh para verificação posterior
79+ ( window as any ) [ eventKey ] = refreshId ;
80+
81+ // Chamar refresh com um ID que podemos verificar
82+ dataSource . refreshData ( ) ;
83+
84+ // Limpar a referência após um tempo
85+ setTimeout ( ( ) => {
86+ delete ( window as any ) [ eventKey ] ;
87+ } , debounceTime * 2 ) ;
88+ } ;
89+
4590 // Efeito para gerenciar eventos do DataSource
4691 useEffect ( ( ) => {
4792 const handleDataSourceEvent = ( event : DataSourceEvent < T > ) => {
48- // Eventos que causam atualização de dados
49- if ( [
93+ // Lista de eventos que causam atualização de dados
94+ const dataUpdateEvents = [
5095 DataSourceEventNames . refreshData ,
5196 DataSourceEventNames . dataChanged ,
5297 DataSourceEventNames . afterRemove ,
5398 DataSourceEventNames . afterSave ,
5499 DataSourceEventNames . afterAppend ,
55100 DataSourceEventNames . afterCancel
56- ] . includes ( event . type ) ) {
101+ ] ;
102+
103+ if ( dataUpdateEvents . includes ( event . type ) ) {
104+ const now = Date . now ( ) ;
105+ const lastProcessed = processedEvents . current . get ( event . type ) || 0 ;
106+
107+ // Verificar se processamos este tipo de evento recentemente
108+ if ( now - lastProcessed < debounceTime ) {
109+ console . log ( `Ignorando evento ${ DataSourceEventNames [ event . type ] } próximo demais ao anterior` ) ;
110+ return ;
111+ }
112+
113+ // Marcar este evento como processado
114+ processedEvents . current . set ( event . type , now ) ;
115+
57116 // Se um componente de input estiver com foco, adiar a atualização
58117 if ( document . activeElement &&
59118 [ 'INPUT' , 'TEXTAREA' , 'SELECT' ] . includes ( document . activeElement . tagName ) ) {
60- // Marcar que há uma atualização pendente
61119 pendingUpdate . current = true ;
62-
63- // Não atualizar imediatamente para evitar perda de foco
64120 return ;
65121 }
66122
67- // Caso contrário, atualizar normalmente com debounce
123+ // Atualizar com debounce para coalescer múltiplos eventos
68124 debouncedUpdateData ( ) ;
69125 }
70126 } ;
71127
72128 // Adicionar listener ao dataSource
73129 dataSource . addListener ( handleDataSourceEvent ) ;
74130
75- // Verificar se há foco em um input e aplicar eventos de monitoramento
131+ // Gerenciamento de foco para preservar experiência de usuário
76132 const handleFocusIn = ( ) => {
77133 const activeElement = document . activeElement ;
78134 if ( activeElement && [ 'INPUT' , 'TEXTAREA' , 'SELECT' ] . includes ( activeElement . tagName ) ) {
@@ -83,14 +139,13 @@ export function useArchbaseDataGridStableRendering<T extends object, ID>({
83139 const handleFocusOut = ( ) => {
84140 blockUpdates . current = false ;
85141
86- // Se houve uma atualização pendente enquanto o input estava em foco, aplicá-la agora
142+ // Aplicar atualizações pendentes após perder o foco
87143 if ( pendingUpdate . current ) {
88144 pendingUpdate . current = false ;
89145 debouncedUpdateData ( ) ;
90146 }
91147 } ;
92148
93- // Tratamento para evitar perda de foco durante digitação
94149 document . addEventListener ( 'focusin' , handleFocusIn ) ;
95150 document . addEventListener ( 'focusout' , handleFocusOut ) ;
96151
@@ -101,13 +156,7 @@ export function useArchbaseDataGridStableRendering<T extends object, ID>({
101156 document . removeEventListener ( 'focusin' , handleFocusIn ) ;
102157 document . removeEventListener ( 'focusout' , handleFocusOut ) ;
103158 } ;
104- } , [ dataSource , debouncedUpdateData ] ) ;
105-
106- // Função para forçar atualização dos dados
107- const refreshData = ( ) => {
108- setIsLoading ( true ) ;
109- dataSource . refreshData ( ) ;
110- } ;
159+ } , [ dataSource , debouncedUpdateData , debounceTime ] ) ;
111160
112161 return {
113162 rows,
0 commit comments