1- import { useEffect , useState , memo , CSSProperties } from 'react' ;
1+ import { useEffect , useState , memo , useMemo , useRef } from 'react' ;
22import { Row } from 'react-table' ;
3- import { areEqual } from 'react-window' ;
3+ import { areEqual , ListChildComponentProps } from 'react-window' ;
44import { useTableContext } from './Tablev2.component' ;
55import {
66 HeadRow ,
@@ -14,7 +14,7 @@ import {
1414 TableLocalType ,
1515 TableVariantType ,
1616} from './TableUtils' ;
17- import { RenderRowType , TableRows , useTableScrollbar } from './TableCommon' ;
17+ import { TableRows , useTableScrollbar } from './TableCommon' ;
1818import useSyncedScroll from './useSyncedScroll' ;
1919import { Box } from '../box/Box' ;
2020import { Loader } from '../loader/Loader.component' ;
@@ -124,79 +124,114 @@ export const MultiSelectableContent = <
124124
125125 const { headerRef } = useSyncedScroll < DATA_ROW > ( ) ;
126126
127- const RenderRow = memo ( ( { index, style } : RenderRowType ) => {
128- const row = rows [ index ] ;
129- prepareRow ( row ) ;
127+ /**
128+ * These values change identity on (almost) every render. We read them through refs so the row
129+ * renderer below can keep a stable identity (see RenderRow).
130+ */
131+ const prepareRowRef = useRef ( prepareRow ) ;
132+ prepareRowRef . current = prepareRow ;
133+ const selectedRowIdsRef = useRef ( selectedRowIds ) ;
134+ selectedRowIdsRef . current = selectedRowIds ;
135+ const onSingleRowSelectedRef = useRef ( onSingleRowSelected ) ;
136+ onSingleRowSelectedRef . current = onSingleRowSelected ;
137+ const toggleAllRowsSelectedRef = useRef ( toggleAllRowsSelected ) ;
138+ toggleAllRowsSelectedRef . current = toggleAllRowsSelected ;
139+ const handleMultipleSelectedRowsRef = useRef ( handleMultipleSelectedRows ) ;
140+ handleMultipleSelectedRowsRef . current = handleMultipleSelectedRows ;
130141
131- const rowProps = {
132- ...row . getRowProps ( {
133- /**
134- * Note:We need to pass the style property to the row component.
135- * Otherwise when we scroll down, the next rows are flashing
136- * because they are re-rendered in loop.
137- */
138- style : { ...style } ,
139- } ) ,
140- onClick : onSingleRowSelected
141- ? ( ) => {
142- onSingleRowSelected ( row ) ;
143- toggleAllRowsSelected ( false ) ;
144- setActiveRowId ( row . id ) ;
145- }
146- : ( ) => handleMultipleSelectedRows ( selectedRowIds , rows , row , index ) ,
147- } ;
142+ /**
143+ * RenderRow MUST keep a stable identity across re-renders. It used to be redefined inline on
144+ * every render, so react-window saw a new component type each time and remounted (not just
145+ * re-rendered) every row — and therefore every cell — whenever the table re-rendered for any
146+ * reason. That made async cell content reload and flash. We now read the row from react-window's
147+ * `data` (itemData) prop and the volatile callbacks/state from refs, so the component is only
148+ * recreated when something that affects the rendered output (activeRowId / separationLineVariant)
149+ * actually changes. Checkbox selection still updates because react-table rebuilds `rows` (and
150+ * therefore `data`) when `selectedRowIds` changes.
151+ */
152+ const RenderRow = useMemo (
153+ ( ) =>
154+ memo ( ( { index, style, data } : ListChildComponentProps < Row < DATA_ROW > [ ] > ) => {
155+ const rows = data ;
156+ const row = data [ index ] ;
157+ prepareRowRef . current ( row ) ;
148158
149- return (
150- < TableRowMultiSelectable
151- { ...rowProps }
152- isSelected = { row . isSelected || activeRowId === row . id }
153- separationLineVariant = { separationLineVariant }
154- className = "tr"
155- >
156- { row . cells . map ( ( cell ) => {
157- const cellProps = cell . getCellProps ( {
158- style : {
159- ...cell . column . cellStyle ,
160- // Vertically center the text in cells.
161- display : 'flex' ,
162- flexDirection : 'column' ,
163- justifyContent : 'center' ,
164- } ,
165- role : 'gridcell' ,
166- } ) ;
159+ const rowProps = {
160+ ...row . getRowProps ( {
161+ /**
162+ * Note:We need to pass the style property to the row component.
163+ * Otherwise when we scroll down, the next rows are flashing
164+ * because they are re-rendered in loop.
165+ */
166+ style : { ...style } ,
167+ } ) ,
168+ onClick : ( ) => {
169+ const onSingleRowSelected = onSingleRowSelectedRef . current ;
170+ if ( onSingleRowSelected ) {
171+ onSingleRowSelected ( row ) ;
172+ toggleAllRowsSelectedRef . current ( false ) ;
173+ setActiveRowId ( row . id ) ;
174+ } else {
175+ handleMultipleSelectedRowsRef . current (
176+ selectedRowIdsRef . current ,
177+ rows ,
178+ row ,
179+ index ,
180+ ) ;
181+ }
182+ } ,
183+ } ;
167184
168- if ( cell . column . id === 'selection' ) {
169- return (
170- < div
171- { ...cellProps }
172- onClick = {
173- onSingleRowSelected
174- ? ( event ) => {
175- event . stopPropagation ( ) ;
176- handleMultipleSelectedRows (
177- selectedRowIds ,
178- rows ,
179- row ,
180- index ,
181- ) ;
182- }
183- : undefined
184- }
185- >
186- { cell . render ( 'Cell' ) }
187- </ div >
188- ) ;
189- }
185+ return (
186+ < TableRowMultiSelectable
187+ { ...rowProps }
188+ isSelected = { row . isSelected || activeRowId === row . id }
189+ separationLineVariant = { separationLineVariant }
190+ className = "tr"
191+ >
192+ { row . cells . map ( ( cell ) => {
193+ const cellProps = cell . getCellProps ( {
194+ style : {
195+ ...cell . column . cellStyle ,
196+ // Vertically center the text in cells.
197+ display : 'flex' ,
198+ flexDirection : 'column' ,
199+ justifyContent : 'center' ,
200+ } ,
201+ role : 'gridcell' ,
202+ } ) ;
203+
204+ if ( cell . column . id === 'selection' ) {
205+ return (
206+ < div
207+ { ...cellProps }
208+ onClick = { ( event ) => {
209+ if ( ! onSingleRowSelectedRef . current ) return ;
210+ event . stopPropagation ( ) ;
211+ handleMultipleSelectedRowsRef . current (
212+ selectedRowIdsRef . current ,
213+ rows ,
214+ row ,
215+ index ,
216+ ) ;
217+ } }
218+ >
219+ { cell . render ( 'Cell' ) }
220+ </ div >
221+ ) ;
222+ }
190223
191- return (
192- < div { ...cellProps } className = "td" >
193- { cell . render ( 'Cell' ) }
194- </ div >
195- ) ;
196- } ) }
197- </ TableRowMultiSelectable >
198- ) ;
199- } , areEqual ) ;
224+ return (
225+ < div { ...cellProps } className = "td" >
226+ { cell . render ( 'Cell' ) }
227+ </ div >
228+ ) ;
229+ } ) }
230+ </ TableRowMultiSelectable >
231+ ) ;
232+ } , areEqual ) ,
233+ [ activeRowId , separationLineVariant ] ,
234+ ) ;
200235
201236 return (
202237 < >
0 commit comments