I have a Chat app. I need to rerender the message item (partial Component) after moderator clicks "Delete". So I send a request to the server to make changes in certain message in array of all messages, and response with updated array. Next I set the new array to State using Reducer. As I can see in the logs, state updates after every response, but React doesn't rerender the message item Component.
Besides, the same code works when I create the new message (example below). Changes on the screen appear only after this sequence: send new message - click "Delete" - send another new message.
Request to the server on "Delete" click:
axios
.patch(`${serverName}/messages/${patchTarget}/${messageId}`)
.then((response) => {
let fetchedMessages = [...response.data];
dispatch(setMessages({ fetchedMessages, flag }));
});
Reducer:
setMessages(state, action) {
const flag = action.payload.flag;
// state.showMessages is responsible for showed messages
state[flag] = [...action.payload.fetchedMessages];
state.showMessages[flag] = [...state[flag]];
}
Server:
let MESSAGES = {
DISPUTE: [{}],
SPEC: [{}]
};
app.get("/messages/:target", (req, res) => {
const fetchTarget = req.params.target;
res.status(200).json(MESSAGES[fetchTarget as keyof typeof MESSAGES]);
});
app.post("/messages/:target", (req, res) => {
const newMessage = req.body;
const pushTarget = req.params.target;
MESSAGES[pushTarget as keyof typeof MESSAGES].push(newMessage);
res.status(200).json(MESSAGES[pushTarget as keyof typeof MESSAGES]);
});
app.patch("/messages/:target/:id", (req, res) => {
const patchTarget = req.params.target;
const messageId = Number(req.params.id);
const element = MESSAGES[patchTarget as keyof typeof MESSAGES][messageId];
if (!element.deletedText) element.deletedText = element.text;
if (!element.deleted) element.deleted = !element.deleted;
element.text = "Message has been deleted by moderator";
res.status(200).json(MESSAGES[patchTarget as keyof typeof MESSAGES]);
});
Working code in another Component where I create a new message and send it:
axios
.post(`${serverName}/messages/${pushTarget}`, newMessageFunc())
.then((response) => {
let fetchedMessages = [...response.data];
dispatch(setMessages({ fetchedMessages, flag }));
});
Why does it work when I create a new message, but it doesn't when I try to edit it?
CodePudding user response:
As long as the data are all right, most likely the issue will be on the front-end side.
There is a lot of unknowns how does this code actually work, but most likely it's the problem with detecting the change in state.
When you have your reducer:
setMessages(state, action) {
const flag = action.payload.flag;
// state.showMessages is responsible for showed messages
state[flag] = [...action.payload.fetchedMessages];
state.showMessages[flag] = [...state[flag]];
}
The arrays have new identity, but the showMessages
object is still the same - it may cause the change being not detected. The React will try to find a change by identity check (===
), not by deep equality check (i.e. lodash
's isEqual
). Assuming that previous state is prevState
and the state after reducer is nextState
:
prevState[flag] === nextState[flag] // false
prevState.showMessages === nextState.showMessages // true
prevState.showMessages[flag] === nextState.showMessages[flag] // false
Most likely, if you'll change your reducer to create a new showMessages
object, it will work fine:
setMessages(state, action) {
const flag = action.payload.flag;
// state.showMessages is responsible for showed messages
state[flag] = [...action.payload.fetchedMessages];
state.showMessages = {
...state.showMessages,
[flag]: [...state[flag]],
};
}
After that, all objects will be detected as different from the previous state:
prevState[flag] === nextState[flag] // false
prevState.showMessages === nextState.showMessages // false
prevState.showMessages[flag] === nextState.showMessages[flag] // false