Handsontable does not render all the rows for a particular column when we try to use updateData or updateSettings to push the changes

Tags: #<Tag:0x00007f51b4cbe5f0> #<Tag:0x00007f51b4cbe4b0>
const { styleId, valueTypeId } = props;

  const dispatch = useAppDispatch();

  const loading = useAppSelector((state) => state.objectStyleParameter.loading);

  const objectStyleParameters = useAppSelector((state) => state.objectStyleParameter.objectStyleParameters);

  const error = useAppSelector((state) => state.objectStyleParameter.error);

  const standards = useAppSelector((state) => state.standard.standards);

  const components = useAppSelector((state) => state.component.components);

  const parameterGroups = useAppSelector((state) => state.parameterGroup.parameterGroups);

  const parameters = useAppSelector((state) => state.parameter.parameters);

  const layers = useAppSelector((state) => state.layer.layers);

  const settings = useAppSelector((state) => state.setting.settings);

  const objectStyles = useAppSelector((state) => state.objectStyle.objectStyles);

  const valueTypes = useAppSelector((state) => state.valueType.valueTypes);

  //set the state of the Progress for Importing as well as the state of the Import

  const [progress, setProgress] = useState(0);

  const [isImporting, setIsImporting] = useState(false);

  //Memoizing the expensive computations for the state

  //Get the standardId for the current objectStyles and memoize it

  const standardId = useMemo(

    () => objectStyles.find((style) => style.Id === styleId)?.StandardId,

    [objectStyles, styleId]

  );

  //Create a parameter map and set it for each ValueTypeId

  const parameterMap = useMemo(() => {

    const map = new Map();

    parameters.forEach((pg) => {

      if (pg.ValueTypeId === valueTypeId) {

        map.set(pg.Id, pg);

      }

    });

    return map;

  }, [parameters, valueTypeId]);

  //filter the parameters and memoize it

  const filteredParameters = useMemo(

    () => parameters.filter((p) => p.ValueTypeId === valueTypeId),

    [parameters, valueTypeId]

  );

  //filter ObjectStyleParameters and memoize them

  const { filteredStyleParameters } = useMemo(

    () => filterStyleParametersByStyleAndParameterMap(objectStyleParameters, parameterMap, styleId),

    [objectStyleParameters, parameterMap, styleId]

  );

  const filteredStyleParametersByValueType = useMemo(

    () => objectStyleParameters.filter((styleParameter) => parameterMap.has(styleParameter.ParameterId)),

    [objectStyleParameters, parameterMap]

  );

  const sortedStyleComponents = sortModels(components, 'Name');

  const sortedParameterGroups = sortModels(parameterGroups, 'Name');

  const sortedParameters = sortModels(filteredParameters, 'Name');

  //This sets the errors in the current handsontable instance

  const [validationErrors, setValidationErrors] = useState<Error[]>([]);

  const [changesMade, setChangesMade] = useState<boolean | undefined>(undefined);

  const [open, setOpen] = useState(false);

  //Create a dropdown Standard State that gets updated when we select a different Standard from the dropdown

  const [dropdownStandardId, setDropdownStandardId] = useState<string>(standardId || '');

  //Updates Dropdown ObjectStyles according to Standard

  const [dropdownStyles, setDropdownStyles] = useState<ObjectStyle[]>([]);

  function sortModels<T>(models: T[], property: keyof T): T[] {

    return [...models].sort((a, b) => {

      const textA = String(a[property]).toUpperCase();

      const textB = String(b[property]).toUpperCase();

      return textA.localeCompare(textB);

    });

  }

  const sortedStyles = sortModels(objectStyles, 'Name');

  const sortedLayers = sortModels(layers, 'Name');

  const handleImportClickOption = () => {

    setOpen(true);

  };

  const handleImportCloseOption = () => {

    setOpen(false);

  };

  const hotTableObjectStyleParameterRef = useRef<HotTable>(null);

  function getExtendedParameterName(parameter: Parameter): string {

    let extendedName = parameter.Name + ' (' + parameter.ValueTypeId + ')';

    return extendedName;

  }

  const layerMap = new Map(layers.map((l) => [l.Id, l.Name]));

  const styleMap = new Map(objectStyles.map((s) => [s.Id, s.Name]));

  function getLayerName(layerId: string | undefined) {

    if (!layerId) return '';

    return layerMap.get(layerId) || '';

  }

  function getStyleName(styleId: string) {

    if (!styleId) return '';

    return styleMap.get(styleId) || '';

  }

  const getParameterName = (parameters: Parameter[], parameterId: string | undefined) => {

    const parameter = parameters.find((p) => p.Id === parameterId);

    return parameter ? getExtendedParameterName(parameter) : '';

  }; 

//Updates the handsontable instance with all the ObjectStyleParameters from all ObjectStyles

  const handleImportClick = async () => {

    const selectedIds = CheckboxStateManager.getSelectedIds();

    setIsImporting(true);

    setProgress(0);

    //Create maps for all the models that we need to lookup

    const parameterMap = new Map(parameters.map((p) => [p.Id, p]));

    const componentMap = new Map(components.map((c) => [c.Id, c.Name]));

    const parameterGroupMap = new Map(parameterGroups.map((pg) => [pg.Id, pg.Name]));

    const groupsToAdd = [];

    for (const selectedId of selectedIds) {

      for (const objectStyleParameter of objectStyleParameters) {

        if (

          objectStyleParameter.ObjectStyleId === selectedId &&

          parameterMap.has(objectStyleParameter.ParameterId) &&

          parameterMap.get(objectStyleParameter.ParameterId)?.ValueTypeId === valueTypeId

        ) {

          let copy = {

            ...objectStyleParameter,

            StyleComponentName:

              componentMap.get(objectStyleParameter.StyleComponentId) || objectStyleParameter.StyleComponentName,

            ParameterName:

              getParameterName(parameters, objectStyleParameter.ParameterId) || objectStyleParameter.ParameterName,

            ParameterGroupName:

              parameterGroupMap.get(objectStyleParameter.ParameterGroupId) || objectStyleParameter.ParameterGroupName,

          };

          const parameter = parameterMap.get(copy.ParameterId);

          if (parameter?.ValueTypeId === VALUETYPES.Style) {

            copy.Value = getStyleName(copy.Value);

          }

          if (parameter?.ValueTypeId === VALUETYPES.Layer) {

            copy.Value = getLayerName(copy.Value);

          }

          groupsToAdd.push(copy);

        }

      }

    }

    if (groupsToAdd.length > 0) {

      const handsontableInstance = hotTableObjectStyleParameterRef.current?.hotInstance;

      if (!handsontableInstance) return;

      let currentTableData = handsontableInstance?.getSourceData() as ObjectStyleParameter[];

      //Update the data in Chunks to show progress

      const chunkSize = groupsToAdd.length > 5000 ? 250 : 100;

      const totalChunks = Math.ceil(groupsToAdd.length / chunkSize);

      // Process chunks with debounced updates

      for (let chunkIndex = 0; chunkIndex < totalChunks; chunkIndex++) {

        const startIdx = chunkIndex * chunkSize;

        const endIdx = Math.min((chunkIndex + 1) * chunkSize, groupsToAdd.length);

        const chunk = groupsToAdd.slice(startIdx, endIdx);

        // Add chunk to currentTableData

        currentTableData = [...currentTableData, ...chunk];

        // Update progress

        const progress = ((chunkIndex + 1) / totalChunks) * 100;

        setProgress(progress);

        // Throttle updates to prevent UI blocking

        if (chunkIndex % 4 === 0 || chunkIndex === totalChunks - 1) {

          await new Promise((resolve) => setTimeout(resolve, 0));

          // Create a new array reference to ensure HandsOnTable detects the change

          const updatedData = JSON.parse(JSON.stringify(currentTableData));

          // Update only the new rows to minimize rendering

          handsontableInstance.updateSettings({

            data: updatedData,

          });

          // Force render the newly added rows

          const lastVisibleRow = handsontableInstance.countRenderedRows();

          if (lastVisibleRow < currentTableData.length) {

            handsontableInstance.scrollViewportTo(lastVisibleRow);

          }

        }

      }

      // Final update to ensure all data is rendered

      handsontableInstance.updateSettings({

        data: JSON.parse(JSON.stringify(currentTableData)),

      });

    }

    handleImportCloseOption();

    CheckboxStateManager.toggleAll(false, []);

    setIsImporting(false);

    setChangesMade(true);

  };

This is the portion of the code where I try to import data where it gets data from two models one from ObjectStyleParameter and from ObjectStyles but when we import the handsontable it only renders the data for ObjectStyleParameter without any issues but the ObjectStyleParameter.Value data does not render. The issue happens we have significantly large data like more than 1000 records. I have tried using just updateData, loadData but to no avail. What can I do?

Hi @ramudarna007_handson

Can you please prepare a minified code demo where the issue reproducible? I am not able to use the code you sent due to the formatting issues.

Does the updateData function have any issues rendering thousands of records when the data is a clubbing of two different data that are set using a type.
Sorry for the late response, I am still trying to mock the data and replicate the issue, but wanted to know whether there is any issue with the updateData function, I also had the same issue with updateSettings and loadData as well?

@ramudarna007_handson

I’m sorry, but I don’t quite understand what is the issue here. In general you can use any data just to show the problem.

We have data from two different interfaces that are combined and shown as a third interface data in the handsontable, when we tried to use updateData to show the data, it only renders few rows then stops rendering the data from the second interface in the combined one

@ramudarna007_handson

Thank you for the explanation, but I would still need to have a code demo to check the problem properly.