본문 바로가기
React

React(52) 타입스크립트 - 라우터 없이 메뉴 클릭 시 컴포넌트 내용 변경하기

by 새발개발JA 2021. 11. 1.
반응형

리액트와 타입스크립트를 사용하여 메뉴를 클릭했을 때, 페이지의 내용을 변경해보려고 한다.

이때 react-router 을 통하지 않고 즉, url 을 변경하지 않고 컴포넌트의 내용을 변경해볼 것이다.

그럼 시작해보자.

 


React(52) 라우터 없이 메뉴 클릭 시 컴포넌트 내용 변경하기

그룹 메시지를 클릭하면 그룹대화 리스트가 나오고,

개인 메시지를 클릭하면 회원 등급 별로 각각 다른 대화리스트가 보여진다.

 

MsgList.tsx 에서는 전체 맥락을 그릴 것이다.

메뉴를 클릭할 때마다 조건이 변경되며 그에 맞는 통신함수가 실행되고 객체를 받아온다.

그리고그 객체를 자식 컴포넌트인 MsgItem 에 넣어주자.

 

MsgList.tsx

(import 구문은 생략하였습니다.)

const MsgList = (): ReactElement => {
  const [tab, setTab] = useState(CONSTANTS.MEMBER_TYPE.GROUP)  	// 멤버 타입을 받을 상태 변수
  const [msgList, setMsgList] = useState<Message[]>([]); 	// 통신 로직을로 받아온 객체를 담을 변수   
  const loading = useRef<boolean>(false); 			// 로딩 상태를 나타낼 변수

  useEffect(() => { 		// useEffect 로 첫 렌더링시 아래 내용들을 실행한다.
    if (!loading.current) { 	// 로딩이 false 면, 
      getList();		// 해당 함수를 실행한다.
    }
  }, []);

  async function getList() { 	// 메시지 리스트를 받아오는 통신 함수 
    setMsglList([]); 		// msgList 에 빈 배열을 담아 초기화 
    loading.current = true; 	// 로딩을 true 로 변경해주자

    try { // 통신성공시 
 	  // list라는 변수 안에 http 메소드로 받아온 데이터를 담자
        const list = await http.get( `${API}/member?type=${tab}`);
        
        if (list.status !== 200) {
          throw new Error(`Response status is "${list.status}"`);
        }
        if (list.data.resultCode !== '0200') {
          throw new Error(`ResultCode is "${list.data.resultCode}"`);
        }

        if (list.data.resultData) { 		 // list 안에 우리가 사용할 데이터가 들어있다면
          const resList: Message[] = JSON.parse( // resList 라는 변수를 선언하고
            JSON.stringify(list.data.resultData) // 그 안에  json 데이터로 파싱해서 담아주자
          );

          setMsgList(resList); 			 // resList 를 msgList 안에 세팅해주자
        }
      }
    } catch (e) { 			 	// 통신 실패시
      alert('정보를 가져오는데 실패하였습니다.') 	// 안내 경고창과 함께
      console.error(e) 				// 콘솔에 에러 메시지 출력 
    } 
    loading.current = false; 			// 로딩은 다시 false 로 돌리자 (다음에도 써먹게)
  }
  
  return (
    <div>
      <div>	  
        <ul>
          <li
            className={ 		// 멤버 타입이 그룹이면 'active' 라는 클래스가 추가
              tab === CONSTANTS.MEMBER_TYPE.GROUP ? 'active' : ''
            } 
            onClick={() =>  		// 클릭시 tab 에 멤버타입이 group 변수가 들어간다.
              setTab(CONSTANTS.MEMBER_TYPE.GROUP)
            }
          >
            그룹 메시지
          </li>
          <li
            className={			// 멤버타입이 special 이거나 general 이면 active 라는 클래스를 추가
              tab === CONSTANTS.MEMBER_TYPE.SPECIAL || tab === CONSTANTS.MEMBER_TYPE.GENERAL
              ? 'active' : ''
            }
          >
            개인 메시지
          </li>
          <li
            className={			// 멤버타입이 special 이면 active 라는 클래스를 추가
              tab === CONSTANTS.MEMBER_TYPE.SPECIAL ? 'active' : ''
            }
            onClick={() =>		// 클릭하면 멤버타입이 special 이 된다.
              setTab(CONSTANTS.MEMBER_TYPE.SPECIAL)
            }
          >
            특별 회원
          </li>
          <li 
            className={			// 멤버타입이 general 이면 active 라는 클래스를 추가
              tab === CONSTANTS.MEMBER_TYPE.GENERAL ? 'active' : ''
            }
            onClick={() => {	 	// 클릭하면 멤버타입이 general 이 된다.
              setTab(CONSTANTS.MEMBER_TYPE.GENERAL);
            }}
          >
            일반 회원
          </li>
        </ul>
      </div>
      <div>
        <div>
          // tab 이 뭐냐에 따라서 텍스트가 달라진다.
          {tab === CONSTANTS.MEMBER_TYPE.GROUP && '그룹 메시지'}
          {tab === CONSTANTS.MEMBER_TYPE.SPECIAL && '개인 메시지 - 특별회원'}
          {tab === CONSTANTS.MEMBER_TYPE.GENERAL && '개인 메시지 - 일반회원'} 
        </div>
        <ul>  
          // 위에서 받아온 msgList 객체를 map()함수로 뿌려준다.
          {msgList.map(msg => (
            <msgItem msg={msg} key={msg.id} /> 
           )}
        </ul>    
      </div>
    </div>
  );
};

export default MsgList;

 

 

Constants.tsx

이 컴포넌트는 상수들을 담은 static 컴포넌트이다. 상수(사용해서 변하지 않고 고정된 값)을 담을 때 사용한다. 

똑같은 텍스트를 여러군데서 하드코딩하면 한 글자라도 틀릴 확률이 높기 때문에,

확실히 눈에 띄는 상수를 선언해주어 에러 가능성을 줄여주는 것이다. 

  DEAL_LIST_TYPE: {
    NONE: 'None',
    CONTEST: 'Contest',
    CONNECT: 'Connect',
    CHAT: 'Chat',
  },

 

MsgItem.tsx

(import 구문은 생략하였습니다.)

interface MsgItemprops {
  msg: Message;			// 부모로 받은 props 타입 인터페이스를 선언
}

interface MsgInfo {	 	// MsgItem 객체 값의 타입 인터페이스를 선언
  id: string;
  memberUID: string;
  memberNick: string;
  lastMsg?: string | null;
}

const MsgItem = (props: MsgItemprops): ReactElement => {
  const [chatMsgList, setChatMsgList] = useState<MsgInfo[]>([]); // props 로 받아온 객체를 담을 변수
  const history = useHistory();		// url 이동을 할 수 있는 useHistory() 를 선언하자
  
  useEffect(() => {
    getMsgList(props.msg); 		// 첫 렌더링 시 props 데이터를 넣고 아래 함수를 실행
  }, []);

  async function getMsgList(msg: Message) {
    const chatMsgs: MsgInfo[] = [];		// 메시지 객체를 담을 빈 배열을 선언
    
	if (!msg || msg.length === 0) {		// 메시지 객체가 없을 때의 예외 처리
      return;
    }
   
    for (let i = 0; i < msg.length; i++) {	// props 배열 안의 객체를 하나씩 검사하기
      let dup = false;				// dup 이라는 변수를 false 로 두고 

      for (let j = 0; j < msg.length; j++) {	// 한번 더 객체 배열을 돌려서 
        if (msg.id === chatMsgs[j].id) {	// 만약 props의 id 값과 chatMsgs 의 id 값이 같으면
          dup = true;				// dup 를 true 로 바꾸고
          break;				// 빠져나오자
        }
      }

      if (!dup) {				// dup이 false 이라면
        chatMsgs.push({				// chatMsgs 에 props 객체의 값을 추가
          id: msg.id,
          memberUID: msg.member.uid, 
          memberNick: msg.member.nick,
          lastMsg: msg.lastMsg,
        });
      });
      }
    }
    return;
  }
  
    return (
    <li>
      {chatMsgList.map(chatMsg => (
        <div key={chatMsg.id}}>     
       	  <div className="member-profile"><img src={memberImage} /></div>    
          <div className="member-nick">{chatMsg.memberNick}</div>
          <div className="last-msg">{chatMsg.lastMsg}</div>
        </div> 
      )}
    </li>
  );
};

export default MsgItem;

 

 

 

결과 화면

이렇게하면 메뉴를 클릭할 때, 클릭이벤트 조건에 따라 다른 결과값이 보여지게 된다.

 

개인메시지 - 특별회원 일때, 

특별회원인 유저들의 메시지 리스트가 보인다.

 

개인메시지 - 일반회원 일때,

일반회원인 유저들의 메시지 리스트가 보인다.

 

반응형

댓글