Reputation: 141
Implemented a chat app and used websocket
. the problem is when the component does mount two connection is created instead of one and whenever a message sends it broadcasts twice. I almost know where the problem lies but don't know how to with it. This is my code and I think it's self-explanatory but if it needs more tell me please. Thanks for your instruction
function ChatScreen(props) {
const client = useRef(null)
const [messages, setMessages] = useState([])
const [value, setValue] = useState('')
const { classes } = props
const location = useLocation()
const [searchParams] = useSearchParams();
const { name, room } = Object.fromEntries([...searchParams])
const navigate = useNavigate()
useEffect(() => {
if (!location.search) {
navigate("/")
return;
}
client.current = new W3CWebSocket('ws://127.0.0.1:8000/ws/chat/' + room + '/')
client.current.onopen = () => console.log("ws opened");
client.current.onclose = () => console.log("ws closed");
}, [location, navigate, room])
useEffect(() => {
if (!client.current) return;
client.current.onmessage = (message) => {
const data = JSON.parse(message.data);
console.log('got reply!');
setMessages(m => [
...m,
{
msg: data.message,
name: data.name,
}
])
}
}, [])
const sendMsgHandler = (e) => {
e.preventDefault()
if (!client.current) {
console.error('No functional connection is established!')
return;
}
client.current.send(
JSON.stringify({
type: "message",
message: value,
name: name
})
);
setValue('')
}
return (...)
}
export default withStyles(useStyles)(ChatScreen)
I think the problem is with the dependency array of useEffect
but if not, what is?
Upvotes: 1
Views: 1814
Reputation: 10849
UseEffect
hook executes when any of the dependency change. Currently
Since the websocket initialization requires only room
params dependency so I would suggest to create a new useEffect hook with only required dependency
useEffect(() => {
client.current = new W3CWebSocket('ws://127.0.0.1:8000/ws/chat/' + room + '/')
client.current.onopen = () => console.log("ws opened");
client.current.onclose = () => console.log("ws closed");
}, [room]);
Also, I would recommend you should define the useEffect
with cleanUp method to dispose the websocket connection. The cleanup is inovked when component gets unmounted.
useEffect(() => {
client.current = new W3CWebSocket('ws://127.0.0.1:8000/ws/chat/' + room + '/')
client.current.onopen = () => console.log("ws opened");
client.current.onclose = () => console.log("ws closed");
return () => {
client.current.close();
};
}, [room]);
Read the details how useEffect works and comparison against class component life cycle events at here.
Upvotes: 2