Hello devs and supporters. In our project we needed to update to the latest version of the library because we have a bug related to the inability to copy from locked cells. This bug was fixed in 14.*
(Copying values from locked cells does not work), but in the major version 14 of the library a regression occurred
(Migration from 13.1.0 to 14.6.1 breaks table when trying to paste values into columns with locked cells) related to fast editing. That’s why I waited for the release of version 15, where this issue has already been resolved.
Now I have encountered a severe performance drop. When initializing the table, the following block of code is executed:
useEffect(() => {
if (
vaultLoadDataStatus === 'IN_PROCESS' ||
inputValues.length === 0 ||
hot === null
)
return
console.time('updateInputResultProps')
updateInputResultProps(hot)
console.timeEnd('updateInputResultProps')
}, [vaultLoadDataStatus, inputValues, plantsListForAside, hot])
In library version 13.1.0 the function updateInputResultProps
executes in about ~230 ms, while in library version 15.1.0 it takes about ~6700 ms—a slowdown by a factor of 30!
Below is the implementation of the function:
updateInputResultProps = (hot: Handsontable) => {
this.vaultSpreadsheet.inputResultProps =
this.vaultSpreadsheet.colNumberPerStation.map((stationColNum, idx) => {
const sumIdx = [...this.inputResultCellIndexes].splice(idx * 3, idx * 3 + 3)
const plantFullData = this.plantsData.find(
(el) => el.plantId === this._displayedPlants[idx]?.plantId
)
const viewOnly = this._displayedPlants[idx]?.viewOnly ?? false
const FLOOD_MODE_WATCH =
this.floodsLeft?.some(
(id) => id === this._displayedPlants[idx]?.plantId
) ?? false
const valueMin = FLOOD_MODE_WATCH
? `=${calcCellFromAlphabet(sumIdx[0] + 1)}25`
: this.inputValues[idx]?.W_MIN
const valueMax = FLOOD_MODE_WATCH
? `=${calcCellFromAlphabet(sumIdx[2] + 1)}25`
: this.inputValues[idx]?.W_MAX
const valueGen = FLOOD_MODE_WATCH
? `=${calcCellFromAlphabet(sumIdx[2] + 1)}25`
: this.inputValues[idx]?.P_GEN_TARGET
const isWMin = this.inputValues[idx]
? Object.keys(this.inputValues[idx])?.some((el) => el === 'W_MIN')
: false
const isWMax = this.inputValues[idx]
? Object.keys(this.inputValues[idx])?.some((el) => el === 'W_MAX')
: false
const isPGen = this.inputValues[idx]
? Object.keys(this.inputValues[idx])?.some(
(el) => el === 'P_GEN_TARGET'
)
: false
const inputValuesRes: Omit<
IVaultStore['inputValues'][0],
'plantId'
> = {
W_MIN: isWMin ? valueMin : undefined,
W_MAX: isWMax ? valueMax : undefined,
P_GEN_TARGET: isPGen ? valueGen : undefined,
}
if (FLOOD_MODE_WATCH) {
console.time('FLOOD_MODE_WATCH')
if (plantFullData?.plantOptimized) {
inputValuesRes['W_MIN'] = hot.getData()[24][sumIdx[0]]
inputValuesRes['W_MAX'] = hot.getData()[24][sumIdx[2]]
} else {
inputValuesRes['P_GEN_TARGET'] = hot.getData()[24][sumIdx[2]]
}
console.timeEnd('FLOOD_MODE_WATCH')
}
console.time('validatePGenTarget')
const validate_P_GEN = validatePGenTarget(
hot,
inputValuesRes,
{
pMin: sumIdx[0],
pGen: sumIdx[1],
pMax: sumIdx[2],
},
isPGen
)
console.timeEnd('validatePGenTarget')
console.time('validateWMin')
const validate_W_MIN = validateWMin(
hot,
inputValuesRes,
{
pMin: sumIdx[0],
pGen: sumIdx[1],
pMax: sumIdx[2],
},
isWMin
)
console.timeEnd('validateWMin')
console.time('validateWMax')
const validate_W_MAX = validateWMax(
hot,
inputValuesRes,
{
pMin: sumIdx[0],
pGen: sumIdx[1],
pMax: sumIdx[2],
},
isWMax
)
console.timeEnd('validateWMax')
const isErrorPGENTARGET = typeof validate_P_GEN !== 'string'
const isErrorWMIN = typeof validate_W_MIN !== 'string'
const isErrorWMAX = typeof validate_W_MAX !== 'string'
return {
stationColNum: stationColNum || 0,
wMin: {
active: isWMin,
disabled:
this._displayedPlants[idx]?.accepted ||
viewOnly ||
this._isLastDay ||
FLOOD_MODE_WATCH ||
plantFullData?.parameters?.E_MAX_E_MIN?.value.turnedOn ||
!this._editMode ||
this._isFinishStage,
value: valueMin,
isValid: isErrorWMIN,
comment:
validate_W_MIN !== undefined
? getStyledComment(validate_W_MIN)
: undefined,
},
pGen: {
active: isPGen,
disabled:
this._displayedPlants[idx]?.accepted ||
viewOnly ||
this._isLastDay ||
FLOOD_MODE_WATCH ||
!this._editMode ||
this._isFinishStage,
value: valueGen,
isValid: isErrorPGENTARGET,
comment:
validate_P_GEN !== undefined
? getStyledComment(validate_P_GEN)
: undefined,
},
wMax: {
active: isWMax,
disabled:
this._displayedPlants[idx]?.accepted ||
viewOnly ||
this._isLastDay ||
FLOOD_MODE_WATCH ||
!this._editMode ||
this._isFinishStage,
value: valueMax,
isValid: isErrorWMAX,
comment:
validate_W_MAX !== undefined
? getStyledComment(validate_W_MAX)
: undefined,
},
}
})
}
I tried to localize the problem and find the bottleneck that caused the slowdown. Apparently, the delay is linked to retrieving data from the table by calling the table instance’s API (hotUp.getData()
), which is present in all of the validator functions.
For example:
const validateWMin = (
hotUp: Handsontable | null,
inputValues: Omit<IVaultStore['inputValues'][0], 'plantId'>,
sumIdx: IValidateSumIdx & { pGen: number },
active: boolean
) => {
console.time('tableDataUp')
const tableDataUp = hotUp?.getData()?.slice(24, 25)[0] ?? []
console.timeEnd('tableDataUp')
// other lines of code
}
const validateWMax = (
hotUp: Handsontable | null,
inputValues: Omit<IVaultStore['inputValues'][0], 'plantId'>,
sumIdx: IValidateSumIdx & { pGen: number },
active: boolean
) => {
console.time('validateWMax_tableDataUp')
const tableDataUp = hotUp?.getData()?.slice(24, 25)[0] ?? []
console.timeEnd('validateWMax_tableDataUp')
// other lines of code
}
const validatePGenTarget = (
hotUp: Handsontable | null,
inputValues: Omit<IVaultStore['inputValues'][0], 'plantId'>,
sumIdx: IValidateSumIdx & { pGen: number },
active: boolean
) => {
console.time('validatePGenTarget_tableDataUp')
const tableDataUp = hotUp?.getData()?.slice(24, 25)[0] ?? []
console.timeEnd('validatePGenTarget_tableDataUp')
// other lines of code
}
I wrapped each line
const tableDataUp = hotUp?.getData()?.slice(24, 25)[0] ?? []
in a timer in the validator functions and observed the following behavior.
13.1.0
15.1.0
Slowing down the execution of each hotUp?.getData()
call by almost 30 times
What is causing such a slowdown of the getData
method? How can this problem be solved?