티스토리 뷰

Edu_hanghae99/TIL

[TIL] 230111

soobin Choi 2023. 1. 11. 13:13

[Error] Each child in a list should have a unique "key" prop.

https://crong-dev.tistory.com/47

 

[Warning: Each child in a list should have a unique "key" prop.] 에러 해결

문제점 아래와 같이 자바스크립트의 map 함수를 사용한 경우 콘솔에서 " Warning: Each child in a list should have a unique "key" prop "이라는 주의 문구를 확인할 수 있다. {["AAA", "BBB", "CCC"].map(item => {item} )}React

crong-dev.tistory.com

너무 간단하게 해결됨.!

 

수정 전

map 돌릴 때 고유한 key 값이 없으면 위의 에러 발생

      <StUserChatBox>
        <div>
          {chatMessages?.map((message) => {
            return <li>{message}</li>;
          })}
        </div>
      </StUserChatBox>

 

수정 후

      <StUserChatBox>
        <div>
          {chatMessages?.map((message) => {
            return <li key={message}>{message}</li>;
          })}
        </div>
      </StUserChatBox>

 

수정 중 1차

      <StUserChatBox>
        {chatData.sender && (
          <div>
            {chatMessages?.map((message) => {
              // 채팅 내역들
              if (chatData.type === 'CHAT') {
                return (
                  <Chats key={message}>{`채팅들 : ${chatMessages}`}</Chats>
                );
              }
              // 내가 채팅 보내는 경우
              if (chatData.sender === nickname) {
                return (
                  <MyChat
                    key={nickname}
                  >{`${chatData.sender}: ${chatData.message}`}</MyChat>
                );
              }
              // 다른 사람이 채팅 보내는 경우
              return (
                <AnotherChat
                  key={nickname}
                >{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
              );
            })}
          </div>
        )}
      </StUserChatBox>

수정 중 2차

<StUserChatBox>
        {chatData.sender && chatData.sender === nickname && (
          <div>
            {chatMessages?.map((nickname) => {
              // 내가 채팅 보내는 경우
              return (
                <MyChat
                  key={nickname}
                >{`${chatData.sender}: ${chatData.message}`}</MyChat>
              );
            })}
          </div>
        )}
        {chatData.sender && chatData.sender !== nickname && (
          <div>
            {chatMessages?.map((nickname) => {
              // 다른 사람이 채팅 보내는 경우
              return (
                <AnotherChat
                  key={nickname}
                >{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
              );
            })}
          </div>
        )}
        {/* // 채팅 내역들
        if (chatData.type === 'CHAT') {
          return (
            <Chats key={message}>{`채팅들 : ${chatMessages}`}</Chats>
          );
        } */}
      </StUserChatBox>

채팅들이 배열에 추가 안 됨

2개의 요소가 들어간 배열이 만들어짐

하나는 제일 처음 채팅 나머지는 입력하는 채팅

 

채팅 리스트 배열 수정 후

setChatMessages((chatMessages) => [...chatMessages, data.message]);

이렇게 바꾸니 새로운 메시지를 입력할 때마다 배열에 데이터가 잘 들어갔다.

 

수정 3차

      <StUserChatBox>
        {chatData.sender && (
          <div>
            <Chats>{`${chatData.sender}: ${chatMessages}`}</Chats>
            {chatData.sender === nickname && (
              <div>
                {chatMessages?.map((nickname) => {
                  // 내가 채팅 보내는 경우
                  return (
                    <MyChat
                      key={nickname}
                    >{`${chatData.sender}: ${chatData.message}`}</MyChat>
                  );
                })}
              </div>
            )}
            {chatData.sender !== nickname && (
              <div>
                {chatMessages?.map((nickname) => {
                  // 다른 사람이 채팅 보내는 경우
                  return (
                    <AnotherChat
                      key={nickname}
                    >{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
                  );
                })}
              </div>
            )}
          </div>
        )}
      </StUserChatBox>

 

수정 4차

      <StUserChatBox>
        {chatData.sender && (
          <div>
            <Chats>{`${chatData.sender}: ${chatMessages}`}</Chats>

            {chatData.sender === nickname && (
              <MyChat>{`${chatData.sender}: ${chatData.message}`}</MyChat>
            )}
            {chatData.sender !== nickname && (
              <AnotherChat>{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
            )}
          </div>
        )}
      </StUserChatBox>

4차 수정 결과

다른 사람이 보낸 메시지면 빨간색, 내가 보낸 메시지면 파란색으로 보임

보라색은 누적되는 쌓이는 채팅 메시지들

 

문제 :  보라색 유저 네임이 채팅을 입력할 때마다 계속 바뀜.

어떻게 해결해야 할까? 실시간 유저 네임은 유저들이 채팅을 입력할 때마다 보여줘야 하고, 쌓이는 채팅 메시지들에는 누가 어떤 메시지를 썼는지 알 수 있어야 하므로, 채팅 메시지들을 배열로 뿌려준 것처럼 누적 유저 네임을 배열로 만들고 메시지와 함께 보여줘야겠다.

 

또 수정

  const [chatUser, setChatUser] = useState([]); // 누적 유저 이름들
  
  ...
  
            case 'CHAT': {
            // console.log('chat', data.sender);
            setChatMessages((chatMessages) => [...chatMessages, data.message]);
            setChatUser((chatUser) => [...chatUser, data.sender]);
            break;
          }
          
         ...
         
       <StUserChatBox>
        {chatData.sender && (
          <div>
            {chatMessages?.map((message, index) => {
              return (
                <Chats>
                  <li
                    key={message}
                  >{`${chatUser[index]}: ${chatMessages[index]}`}</li>
                </Chats>
              );
            })}

            {chatData.sender === nickname && (
              <MyChat>{`${chatData.sender}: ${chatData.message}`}</MyChat>
            )}
            {chatData.sender !== nickname && (
              <AnotherChat>{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
            )}
          </div>
        )}
      </StUserChatBox>

다시 마주한 에러

[Error] Each child in a list should have a unique "key" prop.

잘 작동하지만 에러를 해결해야 함

 

고유한 key 값 주기

      <StUserChatBox>
        {chatData.sender && (
          <div>
            {chatMessages?.map((message, index) => {
              return (
                <Chats key={String(index)}>
                	<div>{`${chatUser[index]}: ${message}`}</div>
                </Chats>
              );
            })}

            {chatData.sender === nickname && (
              <MyChat>{`${chatData.sender}: ${chatData.message}`}</MyChat>
            )}
            {chatData.sender !== nickname && (
              <AnotherChat>{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
            )}
          </div>
        )}
      </StUserChatBox>
key={String(index)}

 

내가 쓴 채팅은 채팅방 오른쪽 다른 사람 채팅은 채팅방 왼쪽으로 보내고 싶음

        {chatData.sender && (
          <div>
            {chatMessages?.map((message, index) => {
              if (chatData.sender === nickname) {
                return (
                  <MyChat
                    // eslint-disable-next-line react/no-array-index-key
                    key={String(index)}
                  >{`${chatUser[index]}: ${message}`}</MyChat>
                );
              }
              return (
                <AnotherChat
                  // eslint-disable-next-line react/no-array-index-key
                  key={String(index)}
                >{`${chatUser[index]}: ${message}`}</AnotherChat>
              );

              // return (
              //   <Chat
              //     // eslint-disable-next-line react/no-array-index-key
              //     key={String(index)}
              //     classname={chatData.sender === nickname ? 'my' : ''}
              //   >
              //     <div>{`${chatUser[index]}: ${message}`}</div>
              //   </Chat>
              // );
            })}

            {/* {chatData.sender === nickname && (
              <MyChat>{`${chatData.sender}: ${chatData.message}`}</MyChat>
            )}
            {chatData.sender !== nickname && (
              <AnotherChat>{`${chatData.sender}: ${chatData.message}`}</AnotherChat>
            )} */}
          </div>
        )}

 

그 사이에 많은 삽질을 했고 3시쯤 완성됐다.

 

채팅 구현 전체 코드

import React, { useEffect, useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import styled from 'styled-components';
import * as SockJs from 'sockjs-client';
import * as StompJs from '@stomp/stompjs';
import { useCookies } from 'react-cookie';
import { getNicknameCookie } from '../../../../utils/cookies';

function ChatBox() {
  const client = useRef({});
  const param = useParams();
  const [cookie] = useCookies();
  const nickname = getNicknameCookie('nickname');
  const [message, setMessage] = useState(''); // input value 값
  const [chatMessages, setChatMessages] = useState([]); // 누적 채팅 메시지들
  const [chatUser, setChatUser] = useState([]); // 누적 유저 이름들
  const connectHeaders = {
    Authorization: cookie.access_token,
    'Refresh-Token': cookie.refresh_token,
  };

  const subscribe = async () => {
    client.current.subscribe(`/sub/gameroom/${param.roomId}`, ({ body }) => {
      const data = JSON.parse(body);
      // console.log('subscribe data', data);
      switch (data.type) {
        case 'ENTER': {
          // console.log('enter');
          break;
        }
        case 'CHAT': {
          // console.log('chat', data.sender)
          setChatMessages((chatMessages) => [...chatMessages, data]);
          setChatUser((chatUser) => [...chatUser, data.sender]);
          // setChatMessages([...chatMessages, data.message]);
          // setChatUser([...chatUser, data.sender]);
          break;
        }
        default: {
          // console.log('default');
          break;
        }
      }
    });
  };

  const connect = () => {
    client.current = new StompJs.Client({
      webSocketFactory: () => new SockJs(`http://13.209.84.31:8080/ws-stomp`),
      connectHeaders,
      debug() {},
      onConnect: () => {
        subscribe();
        client.current.publish({
          destination: `/sub/gameroom/${param.roomId}`,
          body: JSON.stringify({
            type: 'ENTER',
            roomId: param.roomId,
            sender: nickname,
            message: `${nickname}님이 게임에 참가하셨습니다.`,
          }),
        });
      },
      onStompError: (frame) => {
        console.log(`Broker reported error: ${frame.headers.message}`);
        console.log(`Additional details: ${frame.body}`);
      },
    });
    client.current.activate();
  };

  // input value 즉 메시지 채팅을 입력
  function publish(value) {
    if (message === '') {
      alert('채팅 내용을 입력해주세요.');
      return;
    }

    client.current.publish({
      destination: `/sub/gameroom/${param.roomId}`,
      body: JSON.stringify({
        type: 'CHAT',
        roomId: param.roomId,
        sender: nickname,
        message: value,
      }),
    });
    setMessage('');
  }

  useEffect(() => {
    connect();
  }, []);

  return (
    <StChatBox>
      <StNotice>공지내용</StNotice>
      <StUserChatBox>
        <div>
          {chatMessages?.map((message, index) => {
            return (
              <Chat
                // eslint-disable-next-line react/no-array-index-key
                key={String(index)}
                className={message.sender === nickname ? 'my' : 'other'}
              >
                <div>{`${message.sender}: ${message.message}`}</div>
              </Chat>
            );
          })}
        </div>
      </StUserChatBox>
      <StSendChat>
        <input
          type="text"
          placeholder="채팅을 입력해주세요."
          value={message}
          onChange={(e) => setMessage(e.target.value)}
        />
        <button onClick={() => publish(message)}>전송</button>
      </StSendChat>
    </StChatBox>
  );
}

const StChatBox = styled.div`
  border: 2px solid black;
  display: grid;
  grid-template-rows: 50px 1fr 50px;
`;
const StNotice = styled.div`
  border: 1px solid black;
`;
const StUserChatBox = styled.div`
  border: 1px solid black;
`;
const StSendChat = styled.div`
  border: 1px solid black;
  height: 50px;
`;

const Chat = styled.div`
  &.my {
    text-align: right;
    color: blue;
  }

  &.other {
    color: purple;
    text-align: left;
  }
`;

export default ChatBox;

 

공지도 추가해야 하고 더 수정해야 할 듯

'Edu_hanghae99 > TIL' 카테고리의 다른 글

[TIL] 230113  (1) 2023.01.13
[TIL] 230112  (0) 2023.01.13
[TIL] 230110  (1) 2023.01.11
[TIL] 230109  (0) 2023.01.10
[TIL] 방 검색 230106  (0) 2023.01.07