I am facing a problem with re-rendering after a state change in my NextJS app.
The function sendMessageForm
launches a redux action sendMessage
which adds the message to the state.
The problem is unrelated to the returned state in the reducer as I am returning a new object(return {...state}
) which should trigger the re-render!
Is there anything that might block the re-render ?
This is the file that calls & displays the state, so no other file should be responsible ! But if you believe the problem might lie somewhere else, please do mention !
import { AttachFile, InsertEmoticon, Mic, MoreVert } from '@mui/icons-material';
import { Avatar, CircularProgress, IconButton } from '@mui/material';
import InfiniteScroll from 'react-infinite-scroller';
import Head from 'next/head';
import { useState, useEffect } from 'react';
import Message from '../../components/Message.component';
import styles from '../../styles/Chat.module.css'
import { useRouter } from 'next/router'
import {useSelector, useDispatch} from "react-redux"
import {bindActionCreators} from "redux"
import * as chatActions from "../../state/action-creators/chatActions"
const Chat = () => {
const router = useRouter()
const { roomId } = router.query
const auth = useSelector((state)=> state.auth)
const messages = useSelector((state)=> state.chat[roomId].messages)
const dispatch = useDispatch()
const {getMessages, markAsRead, sendMessage} = bindActionCreators(chatActions, dispatch)
const [inputValue, setInputValue] = useState("")
const sendMessageForm = (e) => {
e.preventDefault()
console.log("***inputValue:", inputValue)
sendMessage(roomId, inputValue)
}
const loadMessages = (page) => {
if(roomId)
getMessages(roomId, page)
}
//user-read-message
useEffect(() => {
//user-read-message
markAsRead(roomId, auth.user._id)
}, [messages]);
return (
<div className={styles.container}>
<Head>
<title>Chat</title>
</Head>
<div className={styles.header}>
<Avatar/>
<div className={styles.headerInformation}>
<h3>Zabre el Ayr</h3>
<p>Last Seen ...</p>
</div>
<div className={styles.headerIcons}>
<IconButton>
<AttachFile/>
</IconButton>
<IconButton>
<MoreVert/>
</IconButton>
</div>
</div>
<div className={styles.chatContainer}>
<InfiniteScroll
isReverse={true}
pageStart={0}
loadMore={loadMessages}
hasMore={messages.hasNextPage || false}
loader={<div className={styles.loader} key={0}><CircularProgress /></div>}
>
{Object.keys(messages.docs).map((key, index)=>{
return<Message
key={index}
sentByMe={messages.docs[key].createdBy === auth.user._id}
message={messages.docs[key].msg}
/>})}
</InfiniteScroll>
<span className={styles.chatContainerEnd}></span>
</div>
<form className={styles.inputContainer}>
<InsertEmoticon/>
<input className={styles.chatInput} value={inputValue} onChange={(e)=>setInputValue(e.target.value)}/>
<button hidden disabled={!inputValue} type='submit' onClick={sendMessageForm}></button>
<Mic/>
</form>
</div>)
};
export default Chat;
useSelector
requires a new object with a new reference from the object you are passing to it in order to trigger the re-render
What you're doing with return {...state}
is just creating a new object for the parent object but not the nested one useSelector
is using, which is in your case :
const messages = useSelector((state)=> state.chat[roomId].messages)
So, you should return the whole state as a new object WITH a new state.chat[roomId].messages
object
In other words, the references for the root object & the one being used should be changed.