EasyUI Forum

General Category => EasyUI for React => Topic started by: chrwei on November 28, 2022, 03:19:07 PM



Title: DataGrid dynamic column order rendering different
Post by: chrwei on November 28, 2022, 03:19:07 PM
This example is simplified from my actual use, but represents the pattern.  My Pre and Post functions have a lot more logic.

When you change to type C you can see the column order displayed does not match the column array.

Code:
import './App.css';
import { useState } from 'react';
import { ComboBox, DataGrid, GridColumn, LinkButton } from 'rc-easyui';

function App() {

  const [myData, setData] = useState({
    hasopt2: false,
    info: [
      { title: "Item 1", opt1: 50, opt2: 0, opt3: "Thing 1", opt3e: 25 },
      { title: "Item 2", opt1: 75, opt2: 0, opt3: "Thing 2", opt3e: 45 },
    ],
    selected: {}
  });

  const [currentType, setType] = useState("");
  const [gridCols, setGridCols] = useState([]);


  function ColPre(coltype) {
    let cols = [
      <GridColumn key={"a1"} field="title" title=" " width={140} />,
    ];
    switch (coltype) {
      case "C":
        cols.push(<GridColumn key={"a2"} field="opt1" title={"Option 1"} width={65} />);
        break;
      default: break;
    }
    return cols;
  }

  function ColPost(coltype) {
    function onSelPrice(row) {
      let newdata = { ...myData };
      if (coltype === "C") {
        newdata.selected = {
          title: row.title,
          opt3: row.opt3,
          opt3e: row.opt3e,
        };
      } else {
        newdata.selected = {
          title: row.title,
          opt3: row.opt3,
        };
      }
      setData(newdata);
    }

    let cols = [];
    switch (coltype) {
      case "C":
        cols.push(<GridColumn key={"c1"} field="opt3e" title={"Option 3 X"} width={80} />);
        break;
      default: break;
    }

    cols.push(<GridColumn key={"c2"} field="select" title=" " width={45} render={({ row }) => (<LinkButton onClick={() => onSelPrice(row)}>Use</LinkButton>)} />);
    return cols;
  }

  function GetCols(coltype) {
    return [
      ...ColPre(coltype),
      <GridColumn field="opt2" title={"Option 2"} hidden={!myData.hasopt2} width={65} />,
      <GridColumn field="opt3" title={"Option 3"} />,
      ...ColPost(coltype),
    ];
  }

  return (
    <div className="App">
      <ComboBox value={currentType}
        data={[
          { value: "A", text: "Type A" },
          { value: "B", text: "Type B" },
          { value: "C", text: "Type C" },
        ]}
        onSelectionChange={selection => {
          setType(selection.value);
          setGridCols(GetCols(selection.value));
        }}
      >
      </ComboBox>
      <DataGrid
        style={{ maxHeight: 150, width: 600 }}
        data={myData.info}
      >
        {gridCols}
      </DataGrid>
      Selected: {JSON.stringify(myData.selected)}<br />
      Cols: <pre>{JSON.stringify(gridCols, null, 2)}</pre>
    </div>
  );
}

export default App;

I haven't tried putting every possible column in and using the hidden property, I think that code would be rather cumbersome.

Is there some way to force it to re-render correctly?
   


Title: Re: DataGrid dynamic column order rendering different
Post by: jarry on November 28, 2022, 04:14:11 PM
You have the same 'key' though you change to another column. Please remove the 'key' property, or set it with a different value.
Code:
  function ColPre(coltype) {
    let cols = [
      <GridColumn field="title" title=" " width={140} />,
    ];
    switch (coltype) {
      case "C":
        cols.push(<GridColumn field="opt1" title={"Option 1"} width={65} />);
        break;
      default: break;
    }
    return cols;
  }


Title: Re: DataGrid dynamic column order rendering different
Post by: chrwei on November 29, 2022, 07:19:20 AM
in my real project not having key gives errors about needing keys.

in this test it doesn't error, but it also behaves even worse, the use button is in 2 columns


Title: Re: DataGrid dynamic column order rendering different
Post by: chrwei on November 29, 2022, 07:39:38 AM
this does work, using a ref to make sure each render gets new indexes. though I'm not sure if this will cause issues long term.  the page won't be that long lived, so probably OK in this use case.



Code:
import './App.css';
import { useRef, useState } from 'react';
import { ComboBox, DataGrid, GridColumn, LinkButton } from 'rc-easyui';

function App() {
  const refColIdx = useRef(1);

  const [myData, setData] = useState({
    hasopt2: false,
    info: [
      { title: "Item 1", opt1: 50, opt2: 0, opt3: "Thing 1", opt3e: 25 },
      { title: "Item 2", opt1: 75, opt2: 0, opt3: "Thing 2", opt3e: 45 },
    ],
    selected: {}
  });

  const [currentType, setType] = useState("");
  const [gridCols, setGridCols] = useState([]);


  function ColPre(coltype) {
    let cols = [
      <GridColumn key={refColIdx.current++} field="title" title=" " width={140} />,
    ];
    switch (coltype) {
      case "C":
        cols.push(<GridColumn key={refColIdx.current++} field="opt1" title={"Option 1"} width={65} />);
        break;
      default: break;
    }
    return cols;
  }

  function ColPost(coltype) {
    function onSelPrice(row) {
      let newdata = { ...myData };
      if (coltype === "C") {
        newdata.selected = {
          title: row.title,
          opt3: row.opt3,
          opt3e: row.opt3e,
        };
      } else {
        newdata.selected = {
          title: row.title,
          opt3: row.opt3,
        };
      }
      setData(newdata);
    }

    let cols = [];
    switch (coltype) {
      case "C":
        cols.push(<GridColumn key={refColIdx.current++} field="opt3e" title={"Option 3 X"} width={80} />);
        break;
      default: break;
    }

    cols.push(<GridColumn key={refColIdx.current++} field="select" title=" " width={45} render={({ row }) => (<LinkButton onClick={() => onSelPrice(row)}>Use</LinkButton>)} />);
    return cols;
  }

  function GetCols(coltype) {
    return [
      ...ColPre(coltype),
      <GridColumn key={refColIdx.current++} field="opt2" title={"Option 2"} hidden={!myData.hasopt2} width={65} />,
      <GridColumn key={refColIdx.current++} field="opt3" title={"Option 3"} />,
      ...ColPost(coltype),
    ];
  }

  return (
    <div className="App">
      <ComboBox value={currentType}
        data={[
          { value: "A", text: "Type A" },
          { value: "B", text: "Type B" },
          { value: "C", text: "Type C" },
        ]}
        onSelectionChange={selection => {
          setType(selection.value);
          setGridCols(GetCols(selection.value));
        }}
      >
      </ComboBox>
      <DataGrid
        style={{ maxHeight: 150, width: 600 }}
        data={myData.info}
      >
        {gridCols}
      </DataGrid>
      ColIdx: {refColIdx.current} Selected: {JSON.stringify(myData.selected)}<br />
      Cols: <pre>{JSON.stringify(gridCols, null, 2)}</pre>
    </div>
  );
}

export default App;