Kaoru
Kaoru

Reputation: 113

React state always return initial value

I'm creating web application that receive WebSocket message and log these messages, using React, Next.js.

I have a problem with state. When I reference state, always return initial value.

my code looks like this:

import React, { useState, useEffect } from 'react';
import { Container, Row, Col } from 'react-bootstrap';

export default function Home() {
  let socket = null;
  const [eventLog, setEventLog] = useState([]);
  const [updateFlag, setUpdateFlag] = useState(0);

  const onWebSocketMessage = (e) => {
    const data = JSON.parse(e.data);

    setEventLog(
      [
        {
          title: data.title,
          icon: data.icon,
          body: data.body,
          date: new Date(),
        },
      ].concat(eventLog)
    );

    setUpdateFlag(updateFlag + 1);
    console.log(updateFlag);
  };

  const connectWebSocket = (token) => {
    socket = new WebSocket('wss://localhost/' + token);
    socket.onmessage = onWebSocketMessage;
  };

  useEffect(() => {
    const token = localStorage.getItem('token');
    connectWebSocket(token);
  }, []);

  const eventLogHtml = eventLog.map((obj, index) => (
    <div key={index}>
      <h2>{obj.title}</h2>
      <div>
        <img src={obj.icon} style={{ width: '8rem' }} />
        <span>
          <p>{obj.body}</p>
          <p>{obj.date.toString()}</p>
        </span>
      </div>
    </div>
  ));
  return (
    <>
      <Container>
        <Row>
          <Col>
            <h1>Event log</h1>
            {eventLogHtml}
          </Col>
        </Row>
      </Container>
    </>
  );
}

in this code, eventLog always returns initial value that is []. And console.log(updateFlag); always logs 0.

I expected that eventLog and console.log(updateFlag); increase when receive message, but actually is set only newest one and logs 0.

Any advice would be much appreciated. Thank you!

Upvotes: 1

Views: 2245

Answers (2)

Giang Le
Giang Le

Reputation: 7044

Cause you are for got add dependencies on useEffect. Try this

useEffect(() => {
    const token = localStorage.getItem('token');
    connectWebSocket(token);
}, [eventLog]);

As Dennis said. It reconnect websocket when eventLog change. So you must split connect and listener event.

const token = localStorage.getItem("token");
socket = new WebSocket("wss://localhost/" + token);
//outside function component

useEffect(() => {
  const onMessage = () => {// do stuff}
  socket.on('message', onMessage)

  return () => socket.off('message', onMessage)
}, [eventLog, updateFlag])

Upvotes: 0

Dennis Vash
Dennis Vash

Reputation: 53874

You have few problems like:

  • closure on updateFlag value which won't rerender on the next message
  • reassigning values like socket and connection functions
  • logging a stale state value when setState is async.

To summarize check fixed code:

let socket = null;

export default function Home() {
  const [eventLog, setEventLog] = useState([]);
  const [updateFlag, setUpdateFlag] = useState(0);

  useEffect(() => {
    console.log(updateFlag);
  }, [updateFlag]);

  useEffect(() => {
    const onWebSocketMessage = (e) => {
      const data = JSON.parse(e.data);

      setEventLog((prev) => [
        {
          title: data.title,
          icon: data.icon,
          body: data.body,
          date: new Date(),
        },
        ...prev,
      ]);

      setUpdateFlag((prev) => prev + 1);
    };

    const token = localStorage.getItem("token");
    socket = new WebSocket("wss://localhost/" + token);
    socket.onmessage = onWebSocketMessage;
  }, []);

  ...
}

Upvotes: 2

Related Questions