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>