State delay problem using stompjs and react-query

37 Views Asked by At

I want to get initial state data using https api, react-query
and update it with web socket, stompjs

in this component, 'useGetCheckQuestions' custom hook get the initial data from https api and store it in 'items' state if isLoading finished.

Then it connects to Socket and if it's onConnect (connect success part), subscribe all the item in items array

The problem is, in socket.onConnect items get empty array. I want items get the initial setting which is using custom hook at that moment:

const QuestionCheckModal = ({
  isOpen,
  setIsOpen,
  setIsOpenModal,
}: BaseModalProps) => {
  let { user_id } = useParams();
  const userIdNumber: number = parseInt(user_id, 10);
  const [isHovered, setIsHovered] = useState(false);
  const [items, setItems] = useState([]);
  const [isSocketOpen, setIsSocketOpen] = useState(false);
  const [stompClient, setStompClient] = useState<Stomp.Client | null>(null);

  //Initial data setting
  const checkQuestionData = useGetCheckQuestions(userIdNumber);

  useEffect(() => {
    if (!checkQuestionData.isLoading) {
      console.log('확인 질문 데이터 세팅', checkQuestionData);
      setItems(checkQuestionData.checkQuestion);
    }
  }, [!checkQuestionData.isLoading]);

  const moveItem = (dragIndex: number, hoverIndex: number) => {
    const draggedItem = items[dragIndex];
    setItems((prevItems) => {
      const newItems = [...prevItems];
      newItems.splice(dragIndex, 1);
      newItems.splice(hoverIndex, 0, draggedItem);
      return newItems;
    });
  };

  const handleBtn = () => {
    setIsOpenModal(true);
    setIsOpen(!isOpen);
  };

  /**
   * websocket part
   */
  const token = localStorage.getItem('accessToken');

  //make client
  const socket = new Client({
    brokerURL: `wss://gotchaa.shop/ws`,
    debug: function (str) {
      console.log(str);
    },
    connectHeaders: {
      Authorization: `Bearer ${token}`,
    },
    reconnectDelay: 5000, // 자동 재 연결
    heartbeatIncoming: 4000,
    heartbeatOutgoing: 4000,
  });

  //send msg
  const handlePubQuestion = ({ questionId, questionBody }: QuestionProps) => {
    const strQuestionBody = JSON.stringify(questionBody);

    if (stompClient && stompClient.connected) {
      stompClient.publish({
        destination: `/pub/question/${questionId}`,
        body: strQuestionBody,
        headers: { Authorization: `Bearer ${token}` },
      });
    }
  };
  //get msg
  const handleConnectSubQuestion = (questionId: number) => {
    socket.subscribe(
      `/sub/question/${questionId}`,
      (message: any) => handleGetSubQuestion(message, questionId),
      {
        Authorization: `Bearer ${token}`,
      }
    );
  };
  //메세지 받으면 실행되는 콜백함수
  const handleGetSubQuestion = (message: any, questionId: number) => {
    if (message.body) {
      const parsedBody = JSON.parse(message.body);

      // Find the item with the matching questionId
      const updatedItems = items.map((currentItem) => {
        if (currentItem.id === questionId) {
          // Check the type of the message and update accordingly
          switch (parsedBody.type) {
            case 'IMPORTANCE':
              currentItem.importance = parsedBody.value;
              break;
            case 'CONTENT':
              currentItem.content = parsedBody.value;
              break;
            case 'DELETE':
              // Remove the item from the array
              return null;
            default:
              // Handle other types if needed
              break;
          }
        }
        return currentItem;
      });

      // Remove null values (deleted items) from the array
      const filteredItems = updatedItems.filter((item) => item !== null);

      // Update the state with the modified array
      setItems(filteredItems);

      alert(
        `Updated item with questionId ${questionId}. New state: ${JSON.stringify(
          filteredItems
        )}`
      );
    } else {
      alert('got empty message');
    }
  };

  //connect success
  socket.onConnect = (frame) => {
    console.log('소켓 연결 성공');
    setStompClient(socket);
    console.log(items);

    //item당 열기
    items.forEach(function (item) {
      //구독 소켓 연결
      console.log('열려라' + item.id);
      handleConnectSubQuestion(item.id);
    });
  };

  socket.onStompError = function (frame) {
    console.log('Broker reported error: ' + frame.headers['message']);
    console.log('Additional details: ' + frame.body);
  };

  useEffect(() => {
    console.log('소켓 연결 시작');
    socket.activate();
    return () => {
      //unmount
      console.log('소켓 연결 끝');
      socket.deactivate();
    };
  }, []);

  return (
    <>
      {isOpen && (
        <Container>
          <Topbar>
            <InfoBox>
              <Title>갓차린 면접자 질문 확인</Title>
              <Info
                src={info}
                onMouseEnter={() => setIsHovered(true)}
                onMouseLeave={() => setIsHovered(false)}
              />
              {isHovered && (
                <InfoPopup>
                  면접 질문을 드래그해서 순서를 변경할 수 있어요.
                  <br />
                  여기서 정한 질문 순서대로 면접 중 질문이 나타나요.
                </InfoPopup>
              )}
            </InfoBox>
            <StartBtn onClick={handleBtn}>면접 전형 시작</StartBtn>
          </Topbar>
          <Box>
            <QuestionContainer>
              <DndProvider backend={HTML5Backend}>
                {items.map((item, index) => (
                  <QuestionItemDrag
                    key={item?.id}
                    isCommon={item?.common}
                    content={item?.content}
                    importance={item.importance}
                    index={item?.id}
                    moveItem={moveItem}
                    //wss
                    handleSub={handleConnectSubQuestion}
                    handlePub={handlePubQuestion}
                    isSocketOpen={isSocketOpen}
                    socket={socket}
                  />
                ))}
              </DndProvider>
            </QuestionContainer>
          </Box>
        </Container>
      )}
    </>
  );
};

i tried locate socket activate func after checkQuestionData isLoading is false and items data is set but it works same as before
and tried using useEffect with items dependency so it can activate if items.length is non zero but then it gets problem of infinite activation
then lastly, i changed items to following

checkQuestionData.checkQuestion

then it works but items state get wrong after each render

0

There are 0 best solutions below