0
votes

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

1
"I'm trying to change the state but the state doesn't seems to change" is very vague. Please provide a more detailed explanation of the current and the desired behaviour and include the relevant parts of the code in your question. Also please only ask one question at a time. "I also need this table to have full server side controls for sorting and pagination" is a completely different new question. - trixn
main question was edited, thanks - Jordan
State updates are async. You they are only visible during the next render. You can't see the changed state if you console.log directly after calling setState(). - trixn
i'm pretty sure thats not the case here. i have tried making the console.log inside a useEffect that triggers when the isOpen state changes. but it seems to trigger only once from false to true and thats it - Jordan
State updates, at least if called in a handler, are async. That's a fact. The reason it doesn't work is a) your have not added isOpen as a dependency to your callback. It will never be reconstructed and always do setIsOpen(!false) because false was the value when it was first created and b) you are constructing a component with the handler that you store in state in a useEffect that also only gets called on mount. So your component never changes, including the handler attached to it. Also you should not save elements in state. State should consist of plain data. - trixn

1 Answers

1
votes

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.