React render madness

Lee Z

I am using React hooks and functional components. In my functional component, I have some strange things happening that I cannot figure a way out of. In the useEffect, originally I did not have the return array to prevent an endless loop and was attempting to use rowData within the deleteClickedHandler but it is always empty. After some research said the way around this was to use the useRef, that seemed to work but invoked the ESLinter's anger which demanded to have rowData be in the array that causes re-renders for the useEffect. This causes the deleteClickedHandler to receive the data from rowDataRef.current but causes an endless loop.

The deleteClickedHandler is performing optimistic concurrency, changing the rowData state, and interacting with the api.

After several hours, I do not know how to fix this.

If I remove the rowData from the array that tells useEffect when to re-render, the delete handler works but does not re-render and since deleteClickedHandler is a handler, I cannot determine how to cause the re-render without an endless loop.

I am sure I am doing something stupid but cannot determine why rowData is not available in deleteClickedHandler - I know the handler is a form of a closure but rowData is state.

const initialState = {
  id: 0,
  credit: false,
  transactionAmount: 0,
  accountBalance: 0,
  transactionDate: new Date(),
  confirmation: "",
  comment: "",
  action: "EDIT",
};

const SERVER_URL='/api/checking';  //process.env.REACT_APP_SERVER_URL;

function Checking() {
  const [rowData, setRowData] = useState([]);
  const [newData, setNewData] = useState(initialState);
  const [displayModal, setModalDisplay] = useState(false);
  const columnDefinitions = Columns();
  const rowDataRef = useRef(rowData);

  useEffect(() => {
    fetch(SERVER_URL)
      .then((response) => response.json())
      .then((rowData) => setRowData(rowData));

    rowDataRef.current = rowData;
  }, [rowData]);

  function deleteClickedHandler(props) {
    let fetchUrl = SERVER_URL + "/" + props.id;
    let balance = 0.0;
    let transactionAmount = 0.0;
    let data = rowDataRef.current;

    if (
      data.length > 0 &&
      window.confirm("Are you sure you wish to delete transaction: " + props.id)
    ) {
      for (let i = 0; i < data.length; i++) {
        transactionAmount = props.credit
          ? props.transactionAmount
          : -1.0 * props.transactionAmount;
        balance = balance + transactionAmount;

        if (data[i].id === props.id) {
          data.splice(i, 1);
        }

        if (data[i].id > props.id) {
          data[i].accountBalance = balance;
        }
      }

      setRowData(data);
      CustomFetch(fetchUrl, "DELETE");
    }
  }

  function rowActionHandler(props) {
    switch (props.action) {
      case "DELETE":
        deleteClickedHandler(props);
        break;
      default:
        break;
    }
  }

  const context = { componentParent: rowActionHandler };

Edit 1

The following cleans up the infinite render and allows the delete to access rowData via the rowDataRef, but does not re-render after the delete. How do I get the re-render on the delete to work?

  useEffect(() => {
    fetch(SERVER_URL)
      .then((response) => response.json())
      .then((rowData) => {
        setRowData(rowData)
        rowDataRef.current = rowData;
      });
  }, []);
Lee Z

Though it is not ideal, there is a workaround which is the same thing I was manually doing. This is to force the page to reload using the statement below. If there is someone who can provide a state approach, I am game to changing this answer.

window.location.reload();

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related