inflearn logo
강의

강의

N
챌린지

챌린지

멘토링

멘토링

N
클립

클립

로드맵

로드맵

지식공유

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

Cannot read properties of undefined (reading 'map') 관련 질문드립니다.

329

fefefefe

작성한 질문수 14

0

안녕하세요.

Cannot read properties of undefined (reading 'map') 하여 질문을 드리고자합니다.

문제는 해결 했으나 타입스크립트가 미숙하여 이런 현상이 발생한거 같은데 왜 이런 문제가 발생했는지 잘 몰라서 질문 드립니다.

 

오류가 발생 된 flow는

 

login 시 /workspace/sleact/channel/일반 으로 접을 했을 경우 

 

그림과 같은 에러가 발생하였습니다.

map 관련해서 초기 값이 안들어오는 부분의 에러인것을 확인하고 userData 옵셔널 체이닝을 에 해당 하는 값에 앞부분만 주었는데 에러가 발생한거 같습니다.

 

자세한 코드는 이렇습니다.

 

<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>

 

여기서 Workspaces에도 옵셔널 체이닝을 걸어줘야 에러가 풀렸는데요.

userData?Workspaces?.map...

제로초님의 코딩을 전부 클론하여 작성하였는데 왜 이런 문제가 발생했는지 이해를 못하고 있습니다.

혹여 typescript에서는 전부 옵셔널 체이닝을 적용해줘야하나요 ... ??

 

세부적인 코드는 아래 첨부드립니다.

아 그리고 userData는 정상적으로 다 받아옵니다.

 

import fetcher from '@utils/fetcher';
import axios from 'axios';
import { type } from 'os';
import React, { useCallback, ReactNode, useState } from 'react';
import { Redirect, Route, Switch, useParams } from 'react-router';
import useSWR from 'swr';
import {
  Header,
  RightMenu,
  ProfileImg,
  WorkspaceWrapper,
  Workspaces,
  Channels,
  Chats,
  WorkspaceName,
  MenuScroll,
  ProfileModal,
  LogOutButton,
  WorkspaceButton,
  AddButton,
  WorkspaceModal,
} from '@layouts/Workspace/styles';
import gravatar from 'gravatar';
import Channel from '@pages/Channel';
import DirectMessage from '@pages/DirectMessage';
import Menu from '@components/Menu';
import { Link } from 'react-router-dom';
import { IChannel, IUser, IWorkspace } from '@typings/db';
import { Button, Input, Label } from '@pages/SignUp/styles';
import useInput from '@hooks/useInput';
import Modal from '@components/Modal';
import { toast } from 'react-toastify';
import CreateChannelModal from '@components/CreateChannelModal';

type Props = {
  children?: ReactNode;
};

function Workspace({ children }: Props) {
  const [showUserMenu, setShowUserMenu] = useState(false);
  const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false);

  const [showWorkSpaceModal, setShowWorkSpaceModal] = useState(false);
  const [showCreateChannelModal, setShowCreateChannelModal] = useState(false);

  const [newWorkSpace, onChangeNewSpace, setNewWorkSpace] = useInput('');
  const [newUrl, onChangeNewUrl, setNewUrl] = useInput('');

  const { workspace } = useParams<{ workspace: string }>();

  const {
    data: userData,
    error,
    revalidate,
    mutate,
  } = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher);

  const { data: channelData } = useSWR<IChannel[]>(
    userData ? `http://localhost:3095/api/workspaces/${workspace}/channels` : null,
    fetcher,
  );

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

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

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

  const onCreateWorkspace = useCallback(
    (e) => {
      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(() => {
          revalidate();
          setShowCreateWorkspaceModal(false);
          setNewWorkSpace('');
          setNewUrl('');
        })
        .catch((error) => {
          console.dir(error);
          toast.error(error.response?.data, { position: 'bottom-center' });
        });
    },
    [newWorkSpace, newUrl],
  );

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

  const toggleWorkSpaceModal = useCallback(() => {
    setShowWorkSpaceModal((prev) => !prev);
  }, []);

  const onClickAddChannel = useCallback(() => {
    setShowCreateChannelModal(true);
  }, []);

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

  return (
    <div>
      <Header>
        <RightMenu>
          {/* 우측 상단 프로필 active */}
          <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.email, { 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 onClick={toggleWorkSpaceModal}>Sleact</WorkspaceName>
          <MenuScroll>
            <Menu show={showWorkSpaceModal} onCloseModal={toggleWorkSpaceModal} style={{ top: 95, left: 80 }}>
              <WorkspaceModal>
                <button onClick={onClickAddChannel}>채널만들기</button>
                <button onClick={onLogout}>로그아웃</button>
              </WorkspaceModal>
            </Menu>
            {channelData?.map((v) => (
              <div>{v.name}</div>
            ))}
          </MenuScroll>
        </Channels>
        <Chats>
          <Switch>
            <Route path="/workspace/:workspace/channel/:channel" component={Channel} />
            <Route path="/workspace/:workspace/dm/:id" component={DirectMessage} />
          </Switch>
        </Chats>
      </WorkspaceWrapper>
      {/* 워크 스페이스 생성 모달 onClickCreateWorkspace */}
      <Modal show={showCreateWorkspaceModal} onCloseModal={onCloseModal}>
        <form onSubmit={onCreateWorkspace}>
          <Label id="workspace-label">
            <span>워크스페이스 이름</span>
            <Input id="workspace" value={newWorkSpace} onChange={onChangeNewSpace}></Input>
          </Label>
          <Label id="workspace-label">
            <span>워크스페이스 url</span>
            <Input id="workspace" value={newUrl} onChange={onChangeNewUrl}></Input>
          </Label>
          <Button type="submit">생성하기</Button>
        </form>
      </Modal>

      {/* 채널 만들기 모달 onClickAddChannel*/}
      <CreateChannelModal
        show={showCreateChannelModal}
        onCloseModal={onCloseModal}
        setShowCreateChannelModal={setShowCreateChannelModal}
      />
    </div>
  );
}

export default Workspace;

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

답변 1

0

제로초(조현영)

userData가 있는게 확실하다면 userData.Workspaces가 없었던 것 같습니다.

근데 userData?.Workspaces?.map을 하셔서 최종적으로 문제가 해결된건가요?

워크스페이스 목록들이 전부 뜨나요? 그럼 크게 문제는 없습니다.

0

fefefefe

네네
userData?.Workspaces?.map 해서 해결 했고요.

Workspaces 목록은 array로 담겨 있습니다.

근데 이게 정확히 왜 그런지 모르겠네요.. 값이 있음에 불구하고 

기본 셋팅과 관련하여

0

106

1

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

0

109

2

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

0

165

2

useEffect 개수 관리

0

122

2

라이브러리 서치 방법

0

118

2

함수 정의 패턴

0

80

1

npm run dev 에러

0

156

3

npx webpack 후 에러

0

187

2

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

0

151

2

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

1

192

2

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

0

162

2

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

0

439

3

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

0

608

2

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

0

970

1

배포 방법

0

306

2

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

0

1010

2

alias 경로 설정 오류

0

461

2

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

0

282

1

제네릭 질문

0

225

2

ts-node 대신 tsx 사용여부

0

377

1

배포 관련 질문

0

249

1

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

0

395

2

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

0

341

1

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

0

252

2