inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

Slack 클론 코딩[실시간 채팅 with React]

모달 만들기

Cannot read properties of undefined (reading 'map')

1872

성창수

작성한 질문수 14

0

제로초님, 코드를 따라친 후에 로그아웃을 하고 다시 로그인 하면 이런 에러메세지가 뜹니다.

그런데 네트워크 탭을 보면 로그인이 정상적으로 된거 같아서 새로고침을 하면 에러 메세지가 사라지고 슬랙에서 로그인된 화면이 제대로 뜹니다.

근데 또 여기서 워크스페이스를 생성하려고 하면 콘솔에 axioserror메세지가 떠서 어떻게 해야될지 모르겠습니다..

Workspace/index.tsx

import axios from "axios";
import React, { FC, useCallback, useState } from "react";
import useSWR from 'swr';
import fetcher from "@utils/fetcher";
import { Navigate, Routes, Route } from "react-router-dom";
import { AddButton, Channels, Chats, Header, LogOutButton, MenuScroll, ProfileImg, ProfileModal, RightMenu, WorkspaceButton, WorkspaceName, Workspaces, WorkspaceWrapper } from "@layouts/Workspace/style";
import gravatar from 'gravatar';
import loadable from '@loadable/component';
import Menu from "../../components/Menu";
import Modal from "../../components/Modal";
import { Link } from "react-router-dom";
import { IUser } from "@typings/db";
import { Button, Input, Label } from "@pages/SignUp/styles";
import useInput from "@hooks/useInput";
import {toast} from 'react-toastify';
const Channel = loadable(() => import('@pages/Channel')); 
const DirectMessage = loadable(() => import('@pages/DirectMessage'));

const Workspace: FC<React.PropsWithChildren<{}>> = ({children}) => {
  const [showUserMenu, setShowUserMenu] = useState(false);
  const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false);
  const [newWorkspace, onChangeNewWorkspace, setNewWorkspace] = useInput('');
  const [newUrl, onChangeNewUrl, setNewUrl] = useInput('');
  // revalidate = 서버로 요청 다시 보내서 데이터를 다시 가져옴
  // mutate = 서버에 요청 안보내고 데이터를 수정
  const {data: userData, error, mutate} = useSWR<IUser | false>('/api/users', fetcher, {
    dedupingInterval: 2000,
  });

  const onLogout = useCallback(() => {
    axios.post('/api/users/logout', null , {
      withCredentials: true,
    })
    .then(() => {
      mutate(false, false);
    })
  }, []);

  const onClickUserProfile = useCallback((e: any) => {
    e.stopPropagation();
    setShowUserMenu((prev) => !prev);
  }, [])

  const onClickCreateWorkspace = useCallback(() => {
    setShowCreateWorkspaceModal(true);
  }, [])

  const onCreateWorkspace = useCallback((e: any) => {
    e.preventDefault();
    // 띄어쓰기도 검사해줘야됨
    if(!newWorkspace || !newWorkspace.trim()) return;
    if(!newUrl || !newUrl.trim()) return;
    axios.post('http://localhost:3095/api/workspaces', {
      workspace: newWorkspace,
      url: newUrl,
    }, {
      withCredentials: true,
    })
    .then(() => {
      mutate();
      setShowCreateWorkspaceModal(false);
      setNewWorkspace('');
      setNewUrl('');
    })
    .catch((error) => {
      console.dir(error);
      // 에러가 나면 사용자가 인지하게 해줌
      toast.error(error.response?.data, { position: 'bottom-center' })
    })
  }, [newWorkspace, newUrl])

  const onCloseModal = useCallback(() => {
    setShowCreateWorkspaceModal(false);
  }, [])

  if(!userData) {
    return <Navigate to="/login" />
  }

  if(!userData) return null;

  return(
    <div>
      <Header>
        <RightMenu>
          <span onClick={onClickUserProfile}>
            <ProfileImg src={gravatar.url(userData.email, { s: '28px', d: 'retro' })} alt={userData.nickname} />
            {showUserMenu && <Menu style={{ right: 0, top: 38 }} show={showUserMenu} onCloseModal={onClickUserProfile}>
              <ProfileModal>
                <img src={gravatar.url(userData.nickname, { s: '36px', d: 'retro' })} alt={userData.nickname} />
                <div>
                  <span id="profile-name">{userData.nickname}</span>
                  <span id="profile-active">Active</span>
                </div>
              </ProfileModal>
              <LogOutButton onClick={onLogout}>로그아웃</LogOutButton>
            </Menu>}
          </span>
        </RightMenu>
      </Header>
      <WorkspaceWrapper>
        <Workspaces>
          {userData?.Workspaces.map((ws) => {
            return (
              <Link key={ws.id} to={`/workspace/${123}/channel/일반`}>
                <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton>
              </Link>
            );
          })}
          <AddButton onClick={onClickCreateWorkspace}>+</AddButton>
        </Workspaces>
        <Channels>
          <WorkspaceName>Sleact</WorkspaceName>
          <MenuScroll>menu scroll</MenuScroll>
        </Channels>
        <Chats>
          <Routes>
            <Route path="/channel" element={<Channel />} />
            <Route path="/dm" element={<DirectMessage />} />
          </Routes>
        </Chats>
        {/* Input이 들어있으면 별도의 컴포넌트로 빼는 것을 추천(input에 글자를 입력할 때마다 여기 있는 함수들이 다 리렌더링 되기 때문에 비효율적) */}
      </WorkspaceWrapper>
      <Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}>
        <form onSubmit={onCreateWorkspace}>
          <Label id="workspace-label">
            <span>워크스페이스 이름</span>
            <Input id="workspace" value={newWorkspace} onChange={onChangeNewWorkspace} />
          </Label>
          <Label id="workspace-url-label">
            <span>워크스페이스 이름</span>
            <Input id="workspace" value={newUrl} onChange={onChangeNewUrl} />
          </Label>
          <Button type="submit">생성하기</Button>
        </form>
      </Modal>
    </div>
  )
}

export default Workspace;

Modal/index.tsx

import React, { useCallback, FC } from "react";
import { CloseModalButton, CreateModal } from "./style";

interface Props {
  show: boolean;
  onCloseModal: () => void;
  children: React.ReactNode;
}

const Modal: FC<Props> = ({show, children, onCloseModal}) => {
  const stopPropagation = useCallback((e: any) => {
    e.stopPropagation()
  }, []);

  if(!show){
    return null;
  }
  return(
    <CreateModal onClick={onCloseModal}>
      <div onClick={stopPropagation}>
        <CloseModalButton onClick={onCloseModal}>&times;</CloseModalButton>
        {children}
      </div>
    </CreateModal>
  );
};

export default Modal;

swr2.0 버전, react v18, typescript v18

swr을 최신버전 사용해서 revalidate대신 mutate를 사용했는데 제가 잘못 사용한건지 모르겠습니다.

클론코딩 babel socket.io typescript 웹팩 react 클론코딩 Socket.io

답변 2

0

성창수

userData.Workspaces가 undefined인데 map이 돼서 오류가 난거 같아서 코드를

 

        <Workspaces>
          {userData.Workspaces !== undefined
          ?userData?.Workspaces.map((ws) => {
            return (
              <Link key={ws.id} to={`/workspace/${123}/channel`}>
                <WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton>
              </Link>
            )
          })
          : null
        }
          <AddButton onClick={onClickCreateWorkspace}>+</AddButton>
        </Workspaces>

이렇게 변경하니까 에러가 해결되네요

조언해주셔서 감사합니다!

1

제로초(조현영)

userData?.Workspaces?.map 만 해도 됩니다

0

성창수

오 더 쉬운 방법이 있었네요 감사합니다!

0

제로초(조현영)

네트워크탭에서 빨간색이 있으면 그걸 누르고 헤더를 먼저 보여주세요. 그게 에러 해결의 실마리입니다.

그리고 network error로 되어있으므로 서버 에러 메시지도 보여주세요.

0

성창수

넵!

이게 워크스페이스 생성될 때 오류 헤더이고,

image

서버에서는 이렇게 뜹니다.

image

0

제로초(조현영)

헤더탭 왼쪽에 있는 x를 눌러서 네트워크탭을 보여주세요. 지금 에러메시지가 뭔가 가려져있어서 문제 파악이 안 됩니다. 콘솔에 빨간에러 6개와 추가로 1개가 더 있는데 전부 알아야 합니다.

그리고 api 요청시 3095로 하시는 이유가 있나요? proxy 서버를 사용하세요.

0

성창수

그 마지막 영상 부분에 api 요청을 3095로 바꿔서 그대로 똑같이 바꿨습니다!

말씀해주신대로 proxy서버로 바꿔서 사용했는데 워크스페이스를 생성하면 처음에는 성공하는데 그 후부터는 성공하지 않습니다.

workspace 생성 성공했을 때

workspace 실패했을 때

네트워크 탭입니다

콘솔 에러메세지입니다

0

제로초(조현영)

같은 이름으로 생성하니 안 되는 것 아닌가요?

0

성창수

오 일단 워크스페이스까지는 다 됐습니다 감사합니다!!

근데 로그인 할 때 에러메세지는 왜 뜨는 건가요?

image저 상태에서 새로고침하면 로그인된 화면으로 전환되는데 오류가 아닌건가요?

0

제로초(조현영)

저기 에러 위치가 나와있습니다. 그 부분이 undefined인 겁니다. 새로고침 부분은 지금은 문제인지 알 수 없습니다. 에러부터 해결하고 봐야 합니다.

기본 셋팅과 관련하여

0

91

1

초기 셋팅 back과 front만 남겨두고 다 지운 후 진행 방법

0

96

2

focus 시에만 화면 업데이트 되는 이유 + 해결방법

0

149

2

useEffect 개수 관리

0

109

2

라이브러리 서치 방법

0

103

2

함수 정의 패턴

0

77

1

npm run dev 에러

0

152

3

npx webpack 후 에러

0

178

2

'void' 형식 식의 truthiness를 테스트할 수 없습니다.ts(1345)

0

143

2

사용자 가입시 에러발생 (TypeError: Cannot read properties of null (reading 'addMembers')

1

178

2

초기세팅중 packge.json 에러떠요

0

155

2

CORS - Access-Control-Allow-Origin 누락 문제

0

431

3

로그인 페이지 무한 새로고침 현상

0

598

2

Module not found: Error: Can't resolve './App' 에러

0

955

1

배포 방법

0

296

2

npm run dev 시 빌드가 매우 느려졌습니다

0

989

2

alias 경로 설정 오류

0

448

2

fetcher 함수의 data 값이 두번 찍히는 이유

0

275

1

제네릭 질문

0

217

2

ts-node 대신 tsx 사용여부

0

373

1

배포 관련 질문

0

247

1

[nginx + https] 서비스를 실행하면 niginx가 아닌 서비스 화면을 보여주게 하고 싶습니다.

0

385

2

[배포하기] webpack에 aws 퍼블릭 IPv4 주소 와 포트 주소를 작성하고 나서 빌드후 실행하면 오류가 발생합니다.

0

336

1

users 호출 시 쿠키가 담기지 않는 이슈 질문드립니다.

0

247

2