Reputation: 337
I am integrating web sockets into React with Django in back-end.
I am able to send messages, and receive the new messages from the back-end.
The problem is that after sending 3 or 4 messages, the previous messages are getting replaced by the new messages in the front-end, but all is fine in the back-end, and in fact everything works normally after I refresh.
How to properly update state for situations like this?
The main problem which I found now is, when I send a chat, the state gets updated multiple times, and its length changes everytime and the screen flutters a lot.
slice
name: 'chat',
initialState: {
chatMessages: [],
}
reducers: {
setChatMessages(state, action) {
const chatmessages = action.payload;
return {...state, chatMessages:chatmessages};
},
}
chat
const chatMessagesSelector = useSelector(selectChats);
const chatMessages = chatMessagesSelector?.chatMessages
chatSocket.onmessage = (e) => {
var data = JSON.parse(e.data)
var message = {message: data.message, user: data.user,
timestamp: data.timestamp};
//console.log(message)
let updatedMessages = [...chatMessages];
updatedMessages.push(message);
dispatch(setChatMessages(updatedMessages));
}
This is the chatMessages
object logged above the updatedMessages
.
Array(1)
0: {user: "testuser", message: "check!", timestamp: "2021-03-09T09:15:09.408717+01:00}
length: 1
Upvotes: 4
Views: 3403
Reputation: 19843
The below code (Approach 1*):
chatSocket.onmessage = (e) => {
var data = JSON.parse(e.data)
var message = {message: data.message, user: data.user, timestamp: data.timestamp};
let updatedMessages = [...chatMessages];
updatedMessages.push(message);
dispatch(setChatMessages(updatedMessages));
reads from redux and creates new Array and pushes new element and dispatches the whole array.
Instead of that, (Approach 2), you can just send the new element to be pushed to redux store by the reducer:
chatSocket.onmessage = (e) => {
var data = JSON.parse(e.data)
var message = {message: data.message, user: data.user, timestamp: data.timestamp};
dispatch(setChatMessages(message));
And now (for Approach 2) you need to make this change in reducer:
reducers: {
setChatMessages(state, action) {
return {...state, chatMessages: [...state.chatMessages, action.payload]};}
}
You can also do this:
reducers: {
setChatMessages(state, action) {
state.chatMessages.push(action.payload)
}
because :
Redux Toolkit allows us to write "mutating" logic in reducers. It doesn't actually mutate the state because it uses the Immer library, which detects changes to a "draft state" and produces a brand new immutable state based off those changes
*Approach 1 may be buggy if chatMessages
in your component is outdated or delayed.
The real issue seems to be that your event listener is getting registered multiple times:
To fix it, you should move it into useEffect
or componentDidMount
:
useEffect(() => {
chatSocket.onmessage = (e) => {
var data = JSON.parse(e.data)
// ...
}
return function cleanup() {
// de-register the socket event
}
}, [])
Upvotes: 2
Reputation: 2885
You need to concatenate new messages with old messages, but you are replacing them:
setChatMessages(state, action) {
const chatMessages = action.payload;
return { ...state, chatMessages: state.chatmessages.concat(chatMessages)};
}
Be careful and avoid duplicates, if you store same messages locally and then send them back from socket.
Upvotes: 0