Home > Software engineering >  React, patch call submits older data even after state has updated
React, patch call submits older data even after state has updated

Time:01-07

I am trying to send the state after it has been updated to the database, but for some reason when the .patch is triggered it sends the state prior to the update.

When I do console.log() I can see that the state when saveChangeHandler is called is the previous state, yet the console.log() useSelector gives me the latest state.

I have tried async await but that gave me the same results, I have put the saveChangesHandler inside the updateNodeHandler and that was not fruitful either, what am I missing?

The button calls updateNodeHandler and saveChangesHandler once it has been clicked

                        <button
                          type="submit"
                          disabled
                          // style={{ marginLeft: "-6px" }}
                          onClick={() => {
                            updateNodeHandler(elements);
                            saveChangesHandler();
                          }}
                        >
                          Save Changes
                        </button>

This is my general code:

  const elements = useSelector(state => state.step.NodeElements)

  const updateNodeHandler = () => {
    if (!activeNode) return;
    const element = elements.find((element) => {
      return element.id === activeNode.id
    })
    dispatch(editStep(
          {
            id: activeNode.id,
            text: newtext
          }
    ));
    console.log(elements, "in update nodeshandler")
  };


  const saveChangesHandler = () => {
    const steps = {
      "steps": elements
    }
    console.log(elements)

    getAuthorization()
      .patch(`product/save_data/${ID}/`, steps)
      .catch((err) => {
        console.log("err", err);
      });
  };

This is the slice:

editStep(state: any, action) {
  console.log(action.payload.id);
  return {
    ...state,
    NodeElements: state.NodeElements.map((step) =>
      step.id === action.payload.id
        ? {
            ...action.payload,
          }
        : step
    ),
  };
},

CodePudding user response:

The issue here is that of a stale enclosure. The button's onClick handler has a closure over the current elements state value when the callback is invoked. updateNodeHandler(elements); dispatches an action to update the store, but the component hasn't rerendered yet to pick up the updated elements state value when saveChangesHandler(); is called.

A solution that won't require refactoring any of the handlers and functions would be to import the store object and access the current state in the saveChangesHandler handler.

Exmaple:

import { store } from '../path/to/store';

...

const saveChangesHandler = () => {
  const state = store.getState();
  const elements = state.step.NodeElements;

  const steps = {
    steps: elements,
  };

  console.log(elements);

  getAuthorization()
    .patch(`product/save_data/${ID}/`, steps)
    .catch((err) => {
      console.log("err", err);
    });
};

...

The more correct fix may be to convert saveChangesHandler into an asynchronous action. It could look something like the following:

import { createAsyncThunk } from '@reduxjs/toolkit';

const saveChangesHandler = createAsyncThunk(
  'step/saveChangesHandler',
  ({ id }, { getState, rejectWithValue }) => {
  const state = getState();
  const elements = state.step.NodeElements;

  const steps = {
    steps: elements,
  };

  console.log(elements);

  return getAuthorization()
    .patch(`product/save_data/${id}/`, steps)
    .catch((err) => {
      console.log("err", err);
      rejectWithValue(err);
    });
};
const elements = useSelector(state => state.step.NodeElements)

const updateNodeHandler = (elements) => {
  if (!activeNode) return;
  const element = elements.find((element) => {
    return element.id === activeNode.id
  })
  dispatch(editStep({
    id: activeNode.id,
    text: newtext
  }));
  dispatch(saveChangesHandler({ id: ID }));
};

...

<button
  type="submit"
  onClick={() => {
    updateNodeHandler(elements);
  }}
>
  Save Changes
</button>
  • Related