Hossein Gholami
Hossein Gholami

Reputation: 141

react hooks + websocket

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?

and a snapshot enter image description here

Upvotes: 1

Views: 1814

Answers (1)

user1672994
user1672994

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

Related Questions