Search code examples
reactjsmaterial-uistatematerial-table

Can't change state in Material Table header when using remote data


I've put a simple boolean state on a custom header, the plan is the make this state control a dropdown menu. I've used a simple onClick on this custom header that set the states to it's opposite value and after that logs the state. But the state stays the same and doesn't change on each click.

This happens when I set the columns with an initial setState because I need my table to be fully server side based. When setting the columns with a normal let it works but that's not the particular need.

header is highlighted, click on it and you will see the state log in the console.

export default function PositioningActionsColumn() {
  const [stateData, setStateData] = useState([]);
  const [stateColumns, setStateColumns] = useState([]);
  const [isOpen, setIsOpen] = useState(false);

  // setting the columns and data - will be from a fetch later
  useEffect(() => {
    setStateColumns([
      {
        title: nameHeader,
        field: "name"
      },
      {
        title: "Surname",
        field: "surname",
        initialEditValue: "initial edit value"
      },
      { title: "Birth Year", field: "birthYear", type: "numeric" },
      {
        title: "Birth Place",
        field: "birthCity",
        lookup: { 34: "İstanbul", 63: "Şanlıurfa" }
      }
    ]);
    setStateData([
      { name: "Mehmet", surname: "Baran", birthYear: 1987, birthCity: 63 },
      { name: "Zerya Betül", surname: "Baran", birthYear: 2017, birthCity: 34 }
    ]);
  }, []);

  const orderFunc = (orderedColumnId, orderDirection) => {
    console.log(
      "orderedColumnId:",
      orderedColumnId,
      "orderDirection:",
      orderDirection
    );
  };

  const searchFunc = e => {
    console.log(e);
  };

  const handleClick = () => {
    setIsOpen(!isOpen);
    console.log(isOpen);
  };

  const nameHeader = (
    <div style={{ background: "lightblue" }} onClick={handleClick}>
      Click on this custom Name Header
    </div>
  );

  return (
    <MaterialTable
      title="Editable Preview"
      icons={tableIcons}
      columns={stateColumns}
      data={stateData}
      onOrderChange={(orderedColumnId, orderDirection) => {
        orderFunc(orderedColumnId, orderDirection);
      }}
      onSearchChange={e => searchFunc(e)}
      options={{ draggable: false, selection: true, actionsColumnIndex: -1 }}
      actions={[
        {
          tooltip: "Remove All Selected Users",
          icon: "delete",
          onClick: (evt, data) => {
            console.log({ data });
            alert("You want to delete " + data.length + " rows");
          }
        },
        {
          isFreeAction: true,
          tooltip: "FreeAction",
          icon: () => <div>Free</div>,
          onClick: (evt, data) => {
            console.log({ data });
          }
        }
      ]}
      editable={{
        onRowAdd: newData =>
          new Promise((resolve, reject) => {
            setTimeout(() => {
              {
                console.log({ newData });
                const data = stateData;
                data.push(newData);
                setStateData(data, () => resolve());
              }
              resolve();
            }, 1000);
          }),
        onRowUpdate: (newData, oldData) =>
          new Promise((resolve, reject) => {
            setTimeout(() => {
              {
                console.log({ newData }, { oldData });
                const data = stateData;
                const index = data.indexOf(oldData);
                data[index] = newData;
                setStateData(data, () => resolve());
              }
              resolve();
            }, 1000);
          }),
        onRowDelete: oldData =>
          new Promise((resolve, reject) => {
            setTimeout(() => {
              {
                console.log({ oldData });
                let data = stateData;
                const index = data.indexOf(oldData);
                data.splice(index, 1);
                setStateData(data, () => resolve());
              }
              resolve();
            }, 1000);
          })
      }}
    />
  );
}

https://codesandbox.io/s/material-table-testing-actions-position-and-output-2-sj1tt


Solution

  • Change your call of setIsOpen to:

    const handleClick = useCallback(() => setIsOpen(current => !current), []);
    

    Edit material table testing actions position and output 2

    It will fix that issue. But still it is not recommended to store react elements in state. It should only contain plain data that you use to render the actual elements.