Show the previous content for 5 seconds even if the state updates?

Viewed 122

I have a React application which uses a Django backend, I have used webSocket to connect with the backend which updates state when there are some changes. But the changes are very rapid, so only the last changes are visible. I want to show the previous message for a certain time before next message is displayed. Here is my code

import React, { useEffect, useState, useRef } from "react";

const Text = () => {
  const [message, setMessage] = useState("");
  const webSocket = useRef(null);

  useEffect(() => {
    webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
    webSocket.current.onmessage = (res) => {
      const data = JSON.parse(res.data);
      setMessage(data.message);
    };
    return () => webSocket.current.close();
  }, []);

  return <p>{message}</p>;
};

export default Text;

So the message should be visible for certain time (in seconds, for eg - 5 seconds), then the next message should be shown. Any idea how that could be done?

2 Answers
const Text = () => {
  const [messages, setMessages] = useState([]);
  const currentMessage = messages[0] || "";

  const [timer, setTimer] = useState(null);

  // webSocket ref missing? ;-)

  useEffect(() => {
    webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
    webSocket.current.onmessage = (res) => {
      const data = JSON.parse(res.data);
      setMessages((prevState) => [ ...prevState, data.message]);
    };
    return () => webSocket.current.close();
  }, []);

  // Remove the current message in 5 seconds.
  useEffect(() => {
    if (timer || !messages.length) return;

    setTimer(setTimeout(() => {
      setMessages((prevState) => prevState.slice(1));
      setTimer(null);
    }, 5000));
  }, [messages, timer]);

  return <p>{currentMessage}</p>;
};

You can create a custom hook to handle the message transition. Pass as argument the desired time you want to wait before showing the next message. You can use it in other parts of your code:

useQueu.js

const useQueu = time => {
  const [current, setCurrent] = useState(null); //--> current message
  const queu = useRef([]); //--> messages

  useEffect(() => {
    const timeout = setTimeout(() => {
      setCurrent(queu.current.shift());
    }, time);
    return () => clearTimeout(timeout);
  }, [current]);

  const add = obj => {
    if (!current) setCurrent(obj); //--> don't wait - render immediately
    else {
      queu.current.push(obj);
    }
  };
  return [current, add];
};

Text.js

const Text = () => {
  const [message, add] = useQue(5000);
  const webSocket = useRef(null);

  useEffect(() => {
    webSocket.current = new WebSocket("ws://localhost:8000/ws/some_url/");
    webSocket.current.onmessage = (res) => {
      const data = JSON.parse(res.data);
      add(data.message); //--> add new message
    };
    return () => webSocket.current.close();
  }, []);

  return <p>{message}</p>;
};

Working example

Related