@@ -46,6 +46,7 @@ export interface CellProps {
46
46
cellTooltip ?: string ;
47
47
editMode ?: string ;
48
48
onTableEvent ?: ( eventName : any ) => void ;
49
+ cellIndex ?: string ;
49
50
}
50
51
51
52
export type CellViewReturn = ( props : CellProps ) => ReactNode ;
@@ -65,7 +66,7 @@ const BorderDiv = styled.div`
65
66
left: 0;
66
67
` ;
67
68
68
- const CellWrapper = ( {
69
+ const CellWrapper = React . memo ( ( {
69
70
children,
70
71
tooltipTitle,
71
72
} : {
@@ -82,7 +83,7 @@ const CellWrapper = ({
82
83
return (
83
84
< > { children } </ >
84
85
)
85
- } ;
86
+ } ) ;
86
87
87
88
interface EditableCellProps < T > extends CellProps {
88
89
normalView : ReactNode ;
@@ -108,6 +109,7 @@ function EditableCellComp<T extends JSONValue>(props: EditableCellProps<T>) {
108
109
tableSize,
109
110
textOverflow,
110
111
cellTooltip,
112
+ cellIndex,
111
113
...otherProps
112
114
} = props ;
113
115
@@ -118,15 +120,23 @@ function EditableCellComp<T extends JSONValue>(props: EditableCellProps<T>) {
118
120
const [ tmpValue , setTmpValue ] = useState < T | null > ( value ) ;
119
121
const singleClickEdit = editMode === 'single' ;
120
122
121
- // Use refs to track previous values for comparison
123
+ // Use refs to track component mount state and previous values
124
+ const mountedRef = useRef ( true ) ;
122
125
const prevValueRef = useRef ( value ) ;
123
126
127
+ // Cleanup on unmount
124
128
useEffect ( ( ) => {
125
- console . log ( "rendered EditableCellComp" ) ;
126
- } , [ ] ) ;
129
+ return ( ) => {
130
+ mountedRef . current = false ;
131
+ setTmpValue ( null ) ;
132
+ setIsEditing ( false ) ;
133
+ } ;
134
+ } , [ setIsEditing ] ) ;
127
135
128
136
// Update tmpValue when value changes
129
137
useEffect ( ( ) => {
138
+ if ( ! mountedRef . current ) return ;
139
+
130
140
if ( ! _ . isEqual ( value , prevValueRef . current ) ) {
131
141
setTmpValue ( value ) ;
132
142
prevValueRef . current = value ;
@@ -135,12 +145,15 @@ function EditableCellComp<T extends JSONValue>(props: EditableCellProps<T>) {
135
145
136
146
const onChange = useCallback (
137
147
( value : T ) => {
148
+ if ( ! mountedRef . current ) return ;
138
149
setTmpValue ( value ) ;
139
150
} ,
140
151
[ ]
141
152
) ;
142
153
143
154
const onChangeEnd = useCallback ( ( ) => {
155
+ if ( ! mountedRef . current ) return ;
156
+
144
157
setIsEditing ( false ) ;
145
158
const newValue = _ . isNil ( tmpValue ) || _ . isEqual ( tmpValue , baseValue ) ? null : tmpValue ;
146
159
dispatch (
@@ -153,32 +166,28 @@ function EditableCellComp<T extends JSONValue>(props: EditableCellProps<T>) {
153
166
if ( ! _ . isEqual ( tmpValue , value ) ) {
154
167
onTableEvent ?.( 'columnEdited' ) ;
155
168
}
156
- } , [ dispatch , tmpValue , baseValue , value , onTableEvent ] ) ;
169
+ } , [ dispatch , tmpValue , baseValue , value , onTableEvent , setIsEditing ] ) ;
157
170
158
171
const editView = useMemo (
159
172
( ) => editViewFn ?.( { value, onChange, onChangeEnd, otherProps } ) ?? < > </ > ,
160
173
[ editViewFn , value , onChange , onChangeEnd , otherProps ]
161
174
) ;
162
175
163
176
const enterEditFn = useCallback ( ( ) => {
164
- if ( editable ) setIsEditing ( true ) ;
177
+ if ( ! mountedRef . current || ! editable ) return ;
178
+ setIsEditing ( true ) ;
165
179
} , [ editable , setIsEditing ] ) ;
166
180
167
- // Cleanup function
168
- useEffect ( ( ) => {
169
- return ( ) => {
170
- // Reset state on unmount
171
- setTmpValue ( null ) ;
172
- setIsEditing ( false ) ;
173
- } ;
174
- } , [ setIsEditing ] ) ;
175
-
181
+ // Memoize context values to prevent unnecessary re-renders
182
+ const tagsContextValue = useMemo ( ( ) => candidateTags ?? [ ] , [ candidateTags ] ) ;
183
+ const statusContextValue = useMemo ( ( ) => candidateStatus ?? [ ] , [ candidateStatus ] ) ;
184
+
176
185
if ( isEditing ) {
177
186
return (
178
187
< >
179
188
< BorderDiv className = "editing-border" />
180
- < TagsContext . Provider value = { candidateTags ?? [ ] } >
181
- < StatusContext . Provider value = { candidateStatus ?? [ ] } >
189
+ < TagsContext . Provider value = { tagsContextValue } >
190
+ < StatusContext . Provider value = { statusContextValue } >
182
191
< div className = "editing-wrapper" >
183
192
{ editView }
184
193
</ div >
@@ -189,30 +198,30 @@ function EditableCellComp<T extends JSONValue>(props: EditableCellProps<T>) {
189
198
}
190
199
191
200
return (
192
- < ColumnTypeView
193
- textOverflow = { props . textOverflow }
194
- >
195
- { status === "toSave" && ! isEditing && < EditableChip key = { `editable-chip` } /> }
201
+ < ColumnTypeView
202
+ textOverflow = { props . textOverflow }
203
+ >
204
+ { status === "toSave" && ! isEditing && < EditableChip key = { `editable-chip-${ cellIndex } ` } /> }
205
+ < CellWrapper tooltipTitle = { props . cellTooltip } >
206
+ < div
207
+ key = { `normal-view-${ cellIndex } ` }
208
+ tabIndex = { editable ? 0 : - 1 }
209
+ onFocus = { enterEditFn }
210
+ >
211
+ { normalView }
212
+ </ div >
213
+ </ CellWrapper >
214
+ { /* overlay on normal view to handle double click for editing */ }
215
+ { editable && (
196
216
< CellWrapper tooltipTitle = { props . cellTooltip } >
197
- < div
198
- key = { `normal-view` }
199
- tabIndex = { editable ? 0 : - 1 }
200
- onFocus = { enterEditFn }
201
- >
202
- { normalView }
203
- </ div >
204
- </ CellWrapper >
205
- { /* overlay on normal view to handle double click for editing */ }
206
- { editable && (
207
- < CellWrapper tooltipTitle = { props . cellTooltip } >
208
217
< EditableOverlay
209
- key = " editable-view"
218
+ key = { ` editable-view- ${ cellIndex } ` }
210
219
onDoubleClick = { ! singleClickEdit ? enterEditFn : undefined }
211
220
onClick = { singleClickEdit ? enterEditFn : undefined }
212
221
/>
213
222
</ CellWrapper >
214
- ) }
215
- </ ColumnTypeView >
223
+ ) }
224
+ </ ColumnTypeView >
216
225
) ;
217
226
}
218
227
0 commit comments