Page 1 of 1

[REACT] When updating the values, the data cells are not updated correctly according to the column configuration.

Posted: Thu Aug 21, 2025 8:13 am
by maickcotes

Hello, I am experiencing issues when modifying some data. In some cases, the data cell is not updated correctly, or two objects overlap. I am attaching a video of the project showing the error, along with a screenshot of the overlapping issue. I am also including the complete project so you can run tests.

Video

ForoBryntum.mp4
(12.78 MiB) Downloaded 137 times

image

bryntumImage.png
bryntumImage.png (113.22 KiB) Viewed 5581 times

Project To Test
Please make sure to run the following command:

npm run json-server
npm run dev[
ForoProject.zip
(71.91 KiB) Downloaded 99 times

Re: [REACT] When updating the values, the data cells are not updated correctly according to the column configuration.

Posted: Thu Aug 21, 2025 9:39 am
by tasnim

Hi maickcotes,

Thanks for your report! I had a look at your app and was able to reproduce the issue. I noticed that you’re generating columns and modifying the resource data. At the moment the code is a bit hard for me to follow, so it would really help if you could share a simplified version of it.

If possible, could you create a small test case based on our online demo here: https://bryntum.com/products/schedulerpro/examples/ ?
That would make it much easier for us to investigate and provide a quicker solution.


Re: [REACT] When updating the values, the data cells are not updated correctly according to the column configuration.

Posted: Fri Aug 22, 2025 4:15 pm
by maickcotes

Hi @tasnim to facilitate code tracking, the root problem is in the following path, which is where it should be analyzed: src/utils/configBryntum.tsx. Inside that file, there is the following function, which is responsible for defining the logic of the columns.

export const getColumnsGantt = async (
  ref: ForwardedRef<BryntumGantt>,
  properties?: PropertyDto[] | null
) => {
  const columnStore: ColumnStore = (ref as RefObject<BryntumGantt>).current?.instance
    ?.columns as ColumnStore;
  columnStore?.remove(columnStore.records.filter((r: any) => r?._id !== 'timeaxiscolumn'));

  if (properties) {
    columnStore.add([
      {
        id: 'counter',
        field: 'counter',
        type: 'rownumber',
        cls: 'counter-header',
        cellCls: 'b-border-bottom',
        align: 'center',
        htmlEncodeHeaderText: false,
        hideable: false
      },
      {
        text: 'TestError',
        field: 'testError',
        id: 'TestError',
        minWidth: 40,
        align: 'left',
        groupable: false,
        renderer: ({ record: { data } }: any) => {
          if (!data.resource) {
            return <Skeleton height={"30px"} />;
          }
          return data?.dataRandom;
        }
      },
      {
        text: 'Type',
        field: 'type',
        id: 'Type',
        minWidth: 80,
        align: 'left',
        groupable: false,
        renderer: ({ record: { data } }: any) => {
          if (!data.resource) {
            return <Skeleton height={"30px"} />;
          }
          return data?.resource?.type?.label ?? '';
        }
      },

  ...(properties as PropertyDto[])
    .filter(({ type }) => type === PropertyTypes.IMAGE)
    .map(({ _id: id, name: text }) => ({
      id,
      ...(Object.values(SystemProperties).includes(id as SystemProperties)
        ? {}
        : { hidden: true }),
      field: id,
      text,
      minWidth: 80,
      htmlEncode: false,
      groupable: false,
      alwaysClearCell: true,
      renderer: async ({ record: { data }, cellElement }: { record: any, cellElement: any }) => {

        if (!data?.resource) {
          // const container = document.createElement('div');
          // cellElement.appendChild(container);

          // const root = createRoot(container);
          // root.render(<Skeleton width={90} height={30} sx={{ padding: '0em' }} />);
          return <Skeleton width={"90px"} height={"30px"} />
        }
        else {
          const src = data[id];
          if (!src) {
            cellElement.innerHTML = '';
          }
          // if (src !== cellElement.dataset.src) {
          //   
          // }
          if (imageIsExpired(src)) {
            const containerV2 = document.createElement('div');
            containerV2.style.cssText = `
              display: flex;
              justify-content: center;
              align-items: center;
              width: 100%;
              height: 100%;
            `;
            cellElement.appendChild(containerV2);

            // const root = createRoot(containerV2);
            // root.render(
            //   <div
            //     style={{ height: `28px`, width: `28px`, backgroundColor: "black" }}
            //   />
            // );
          }
          const imageUrl = await getCachedOrFetchImage(src);
          if (imageUrl) {


            cellElement.innerHTML = `
                  <div style="
                    display: flex;
                    justify-content: center;
                    align-items: center;
                    height: 100%;
                    width: 100%;
                    padding: 5px;">
                    <img
                      src="${imageUrl}"
                      alt="Resource"
                      style="
                        width: 30px;
                        height: 30px;
                        border-radius: 50%;
                        object-fit: cover;
                        display: block;
                      "
                    />
                  </div>
                `;
          }
          return
        }
      }
    })),

  ...(properties as PropertyDto[])
    .filter(({ type }) => type !== PropertyTypes.IMAGE)
    .map(({ _id: id, name: text, type }) => ({
      id,
      ...(Object.values(SystemProperties).includes(id as SystemProperties)
        ? {}
        : { hidden: true }),
      field: id,
      text,
      minWidth: 80,
      htmlEncode: false,
      align: type === PropertyTypes.NUMBER ? 'right' : 'left',
      groupable: false,

      renderer: async ({ record: { data }, cellElement }: { record: any, cellElement: any }) => {
        if (!data.resource) {
          return <Skeleton height={"30px"} />
        }
        else {
          return transformPropertyValue(type, data[id])
        }


      }
    })),

  {
    text: "misision_others",
    field: 'missions',
    id: 'missions',
    minWidth: 80,
    align: 'left',

    groupable: false,
    renderer: ({ record: { data } }: any) => {
      if (!data.resource) {
        return <Skeleton height={"30px"} />;
      }
      return data?.resource?.missions?.label ?? '';
    }
  },
  {
    text: "work rules",
    field: 'workRules',
    id: 'workRules',
    flex: 1,
    align: 'left',

    groupable: false,
    renderer: ({ record: { data } }: any) => {
      if (!data.resource) {
        return <Skeleton height={"30px"} />;
      }
      return data?.resource?.workRules?.label ?? '';
    }
  }
]);
await columnStore.commit();
  } else {
    columnStore.add([
      ...new Array(8).fill('').map(() => ({
        text: '...',
        width: 80,
        renderer: () => {
          return <Skeleton height={"30px"} />;
        }
      }))
    ]);
    await columnStore.commit();
  }

};

If you notice, the specific section of those columns that keeps loading is this:

 ...(properties as PropertyDto[])
        .filter(({ type }) => type !== PropertyTypes.IMAGE)
        .map(({ _id: id, name: text, type }) => ({
          id,
          ...(Object.values(SystemProperties).includes(id as SystemProperties)
            ? {}
            : { hidden: true }),
          field: id,
          text,
          minWidth: 80,
          htmlEncode: false,
          align: type === PropertyTypes.NUMBER ? 'right' : 'left',
          groupable: false,

      renderer: async ({ record: { data }, cellElement }: { record: any, cellElement: any }) => {
        if (!data.resource) {
          return <Skeleton height={"30px"} />
        }
        else {
          return transformPropertyValue(type, data[id])
        }


      }
    })),

It can be said that when there are changes in that data (data.resource), this part is not updating.


Re: [REACT] When updating the values, the data cells are not updated correctly according to the column configuration.

Posted: Fri Aug 22, 2025 5:21 pm
by tasnim

Thanks for the explanation, this needs further investigation. Seems it doesn't work when it returns JSX renderer
I've opened a ticket to investigate it here https://github.com/bryntum/support/issues/11813


Re: [REACT] When updating the values, the data cells are not updated correctly according to the column configuration.

Posted: Mon Sep 15, 2025 10:45 am
by saki

Hello @maickcotes,

I've started and run your showcase and I cannot figure out where the problem is. I'd like to compare the expected and actual behaviors which approach always help to zero in on the possible culprit. Here I'm missing virtually both as I have no clue how should it look if it worked and it's also difficult to evaluate those flickering rectangles what I get after clicking update data. Is it a loading indicator so that data never got loaded? Should they stay there but with different width?

One way or another, I'd need a simple showcase that would indicate the problem clearly.

Some thoughts:

  1. It is quite unusual to remove all columns and re-generate them in an async function. There can always be a risk a racing condition with loading data. It's be better to do atomic sync changes here: a) remove columns, b) add new columns, c) load data if needed.
  2. In a renderer, I'd rather always return a JSX component and I'd handle conditional rendering inside the React component.
  3. Non-JSX renderers are meant to return a value (string or DomConfig object) that will be consequently used as the cell's content. Manipulating cellElement, appending to it, setting innerHTML on DOM elements is always risky. You should not need to create/remove/change DOM elements in the renderer.

So give us please a simplest possible showcase that would demonstrate a bug we may potentially have in the rendering logic. Show please the desired and actual results.