-
카테고리
-
세부 분야
프론트엔드
-
해결 여부
미해결
Chats안에 Route된 페이지가 화면에 출력 되지 않는 현상
22.11.05 22:13 작성 조회수 224
0
import {
AddButton, Channels, Chats, Header, LogOutButton, MenuScroll, ProfileImg,
ProfileModal, RightMenu, WorkspaceButton, WorkspaceModal, WorkspaceName, Workspaces, WorkspaceWrapper
} from '@layouts/Workspace/styles'
import fetcher from '@utils/fetcher';
import React, { useCallback, useState } from 'react';
import useSWR from 'swr';
import axios from 'axios'
import gravatar from 'gravatar'
import loadable from '@loadable/component';
import { Navigate, Route, Routes } from 'react-router';
import { useParams } from 'react-router';
import { toast } from 'react-toastify';
import Menu from '@components/Menu';
import useInput from '@hooks/useInput';
import Modal from '@components/Modal';
import CreateChannelModal from '@components/CreateChannelModal';
import { IChannel, IUser } from '@typings/db';
import { Link } from 'react-router-dom';
import { Button, Input, Label } from '@pages/SignUp/styles';
import InviteWorkspaceModal from '@components/InviteWorkspaceModal';
import InviteChannelModal from '@components/InviteChannelModal';
import ChannelList from '@components/ChannelList';
import DMList from '@components/DMList';
const Channel = loadable(() => import('@pages/Ch'));
const DirectMessage = loadable(() => import('@pages/DirectMessage'));
const Workspace = () => {
// const Workspace: React.FC<Props> = ({ children }) => {
// const { data: userData, mutate: revalidateUser } = useSWR<IUser | false>('http://localhost:3095/api/users', fetcher);
const [showUserMenu, setShowUserMenu] = useState(false);
const [showCreateWorkspaceModal, setShowCreateWorkspaceModal] = useState(false);
const [showInviteWorkspaceModal, setShowInviteWorkspaceModal] = useState(false);
const [showInviteChannelModal, setShowInviteChannelModal] = useState(false);
const [showWorkspaceModal, setShowWorkspaceModal] = useState(false);
const [showCreateChannelModal, setShowCreateChannelModal] = useState(false);
const [newWorkspace, onChangeNewWorkspace, setNewWorkpsace] = useInput('');
const [newUrl, onChangeNewUrl, setNewUrl] = useInput('');
const { workspace } = useParams<{ workspace: string }>();
const { data: userData, error, mutate: revalidateUser } = useSWR<IUser | false>('/api/users', fetcher, {
dedupingInterval: 2000,
});
const { data: channelData } = useSWR<IChannel[]>(
userData ? `/api/workspaces/${workspace}/channels` : null, fetcher);
const onLogout = useCallback(() => {
console.log(userData)
axios.post(
'/api/users/logout', null, { withCredentials: true, })
.then(() => {
revalidateUser(false, false);
})
.catch((err) => {
console.log(err)
})
}, [])
const onClickUserProfile = useCallback(() => {
setShowUserMenu((prev) => !prev);
}, [])
const onCloseUserProfile = useCallback((e: React.MouseEvent<HTMLInputElement>) => {
// console.trace('click')
e.stopPropagation();
setShowUserMenu((prev) => !prev);
}, [])
const onClickCreateWorkspace = useCallback(() => {
setShowCreateWorkspaceModal(true);
}, [])
// 메뉴창에 채널생성
const onCreateWorkspace = useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!newWorkspace || !newWorkspace.trim()) return;
if (!newUrl || !newUrl.trim()) return;
axios.post('/api/workspaces', {
workspace: newWorkspace,
url: newUrl,
}, {
// 내가 로그인 된 상태라는걸 쿠키를 전달해서 안다
withCredentials: true,
}).then(() => {
revalidateUser();
// 초기화
setShowCreateWorkspaceModal(false);
setNewWorkpsace('');
setNewUrl('');
}).catch((error) => {
console.dir(error);
// toastify npm으로 에러 메세지
toast.error(error.response?.data, { position: 'bottom-center' })
})
}, [newWorkspace, newUrl])
// 모달닫기
const onCloseModal = useCallback(() => {
setShowCreateWorkspaceModal(false);
setShowCreateChannelModal(false);
setShowInviteWorkspaceModal(false);
setShowInviteChannelModal(false);
}, [])
const toggleWorkspaceModal = useCallback(() => {
setShowWorkspaceModal((prev) => !prev);
}, [])
const onClickAddChannel = useCallback(() => {
setShowCreateChannelModal(true);
}, [])
const onClickInviteWorkspace = useCallback(() => {
setShowInviteWorkspaceModal(true);
}, []);
// return 아래에 hooks가 있으면 Invalid hook call 에러가 뜬다
if (userData === undefined) return null;
if (userData === false) {
return <Routes><Route path="/*" element={<Navigate replace to="/login" />} /></Routes>
}
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={onCloseUserProfile}>
<ProfileModal>
<img src={gravatar.url(userData.email, { s: '28px', 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 && userData?.Workspaces.map((ws: any) => {
return (
<Link key={ws.id} to={`/workspace/${ws.url}/channel/일반`}>
<WorkspaceButton>{ws.name.slice(0, 1).toUpperCase()}</WorkspaceButton>
</Link>
);
})}
<AddButton onClick={onClickCreateWorkspace}>+</AddButton>
</Workspaces>
<Channels>
<WorkspaceName onClick={toggleWorkspaceModal}>Sleact</WorkspaceName>
<MenuScroll>
{/* Menu에서 div옆에 style을 받았기 때문에 style사용가능 */}
<Menu show={showWorkspaceModal} onCloseModal={toggleWorkspaceModal} style={{ top: 95, left: 80 }}>
<WorkspaceModal>
<h2>Sleact</h2>
<button onClick={onClickInviteWorkspace}>워크스페이스에 사용자 초대</button>
<button onClick={onClickAddChannel}>채널 만들기</button>
<button onClick={onLogout}>로그아웃</button>
</WorkspaceModal>
</Menu>
<ChannelList />
<DMList />
</MenuScroll>
</Channels>
<Chats>
<Routes>
<Route path='/workspace/:workspace/channel/:channel/*' element={<><Channel /></>} />
<Route path='/workspace/:workspace/dm/:id/*' element={<><DirectMessage /></>} />
</Routes>
</Chats>
</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>워크스페이스 url</span>
<Input id="workspace" value={newUrl} onChange={onChangeNewUrl} />
</Label>
<Button type="submit">생성하기</Button>
</form>
</Modal>
<CreateChannelModal show={showCreateChannelModal} onCloseModal={onCloseModal}
setShowCreateChannelModal={setShowCreateChannelModal} />
<InviteWorkspaceModal show={showInviteWorkspaceModal} onCloseModal={onCloseModal} setShowInviteWorkspaceModal={setShowInviteWorkspaceModal} />
<InviteChannelModal show={showInviteChannelModal} onCloseModal={onCloseModal} setShowInviteChannelModal={setShowInviteChannelModal} />
</div>
)
}
export default Workspace;
Workspace
import React from 'react';
import gravatar from 'gravatar';
import { Container, Header } from './styles';
import { useParams } from 'react-router';
import useSWR from 'swr';
import fetcher from '@utils/fetcher';
import ChatBox from '@components/ChatBox';
import ChatList from '@components/ChatList';
const DirectMessage = () => {
const { workspace, id } = useParams<{ workspace: string; id: string }>();
const { data: userData } = useSWR(`/api/workspaces/${workspace}/users/${id}`, fetcher);
const { data: myData } = useSWR('/api/users', fetcher);
if (!userData || !myData) {
return null
}
return (
<Container>
<Header>
<img src={gravatar.url(userData.email, { s: '24px', d: 'retro' })} alt={userData.nickname} />
<span>{userData.nickname}</span>
</Header>
<ChatList />
<ChatBox chat="" />
</Container>
)
}
export default DirectMessage;
DirectionMessage
import React, { useCallback } from 'react'
import { Form } from 'react-router-dom';
import { ChatArea, MentionsTextarea, SendButton, Toolbox } from './styles';
interface Props {
chat: string;
}
const ChatBox: React.FC<Props> = ({ chat }) => {
const onSubmitForm = useCallback(() => {
}, []);
return (
<ChatArea>
<Form onSubmit={onSubmitForm}>
<MentionsTextarea>
<textarea />
</MentionsTextarea>
<Toolbox>
<SendButton
className={
'c-button-unstyled c-icon_button c-icon_button--light c-icon_button--size_medium c-texty_input__button c-texty_input__button--send' +
(chat?.trim() ? '' : ' c-texty_input__button--disabled')
}
data-qa="texty_send_button"
aria-label="Send message"
data-sk="tooltip_parent"
type="submit"
disabled={!chat?.trim()}
>
<i className="c-icon c-icon--paperplane-filled" aria-hidden="true" />
</SendButton>
</Toolbox>
</Form>
</ChatArea>
)
}
export default ChatBox;
ChatBox
import styled from '@emotion/styled';
export const Container = styled.div`
display: flex;
flex-wrap: wrap;
height: calc(100vh - 38px);
flex-flow: column;
position: relative;
`;
export const Header = styled.header`
height: 64px;
display: flex;
width: 100%;
--saf-0: rgba(var(--sk_foreground_low, 29, 28, 29), 0.13);
box-shadow: 0 1px 0 var(--saf-0);
padding: 20px 16px 20px 20px;
font-weight: bold;
align-items: center;
& img {
margin-right: 5px;
}
`;
export const DragOver = styled.div`
position: absolute;
top: 64px;
left: 0;
width: 100%;
height: calc(100% - 64px);
background: white;
opacity: 0.7;
display: flex;
align-items: center;
justify-content: center;
font-size: 40px;
`;
DirectMessage styles
import styled from '@emotion/styled';
import { MentionsInput } from 'react-mentions';
export const ChatArea = styled.div`
display: flex;
width: 100%;
padding: 20px;
padding-top: 0;
`;
export const Form = styled.form`
color: rgb(29, 28, 29);
font-size: 15px;
width: 100%;
border-radius: 4px;
border: 1px solid rgb(29, 28, 29);
`;
export const MentionsTextarea = styled(MentionsInput)`
font-family: Slack-Lato, appleLogo, sans-serif;
font-size: 15px;
padding: 8px 9px;
width: 100%;
& strong {
background: skyblue;
}
& textarea {
height: 44px;
padding: 9px 10px !important;
outline: none !important;
border-radius: 4px !important;
resize: none !important;
line-height: 22px;
border: none;
}
& ul {
border: 1px solid lightgray;
max-height: 200px;
overflow-y: auto;
padding: 9px 10px;
background: white;
border-radius: 4px;
width: 150px;
}
`;
export const Toolbox = styled.div`
position: relative;
background: rgb(248, 248, 248);
height: 41px;
display: flex;
border-top: 1px solid rgb(221, 221, 221);
align-items: center;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
`;
export const SendButton = styled.button`
position: absolute;
right: 5px;
top: 5px;
`;
export const EachMention = styled.button<{ focus: boolean }>`
padding: 4px 20px;
background: transparent;
border: none;
display: flex;
align-items: center;
color: rgb(28, 29, 28);
width: 100%;
& img {
margin-right: 5px;
}
${({ focus }) =>
focus &&
`
background: #1264a3;
color: white;
`};
`;
ChatBox styles
import styled from '@emotion/styled';
export const RightMenu = styled.div`
float: right;
`;
export const Header = styled.header`
height: 38px;
background: #350d36;
color: #ffffff;
box-shadow: 0 1px 0 0 rgba(255, 255, 255, 0.1);
padding: 5px;
text-align: center;
`;
export const ProfileImg = styled.img`
width: 28px;
height: 28px;
position: absolute;
top: 5px;
right: 16px;
`;
export const ProfileModal = styled.div`
display: flex;
padding: 20px;
& img {
display: flex;
}
& > div {
display: flex;
flex-direction: column;
margin-left: 10px;
}
& #profile-name {
font-weight: bold;
display: inline-flex;
}
& #profile-active {
font-size: 13px;
display: inline-flex;
}
`;
export const LogOutButton = styled.button`
border: none;
width: 100%;
border-top: 1px solid rgb(29, 28, 29);
background: transparent;
display: block;
height: 33px;
padding: 5px 20px 5px;
outline: none;
cursor: pointer;
`;
export const WorkspaceWrapper = styled.div`
display: flex;
flex: 1;
`;
export const Workspaces = styled.div`
width: 65px;
display: inline-flex;
flex-direction: column;
align-items: center;
background: #3f0e40;
border-top: 1px solid rgb(82, 38, 83);
border-right: 1px solid rgb(82, 38, 83);
vertical-align: top;
text-align: center;
padding: 15px 0 0;
`;
export const Channels = styled.nav`
width: 260px;
display: inline-flex;
flex-direction: column;
background: #3f0e40;
color: rgb(188, 171, 188);
vertical-align: top;
& a {
padding-left: 36px;
color: inherit;
text-decoration: none;
height: 28px;
line-height: 28px;
display: flex;
align-items: center;
&.selected {
color: white;
}
}
& .bold {
color: white;
font-weight: bold;
}
& .count {
margin-left: auto;
background: #cd2553;
border-radius: 16px;
display: inline-block;
font-size: 12px;
font-weight: 700;
height: 18px;
line-height: 18px;
padding: 0 9px;
color: white;
margin-right: 16px;
}
& h2 {
height: 36px;
line-height: 36px;
margin: 0;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 15px;
}
`;
export const WorkspaceName = styled.button`
height: 64px;
line-height: 64px;
border: none;
width: 100%;
text-align: left;
border-top: 1px solid rgb(82, 38, 83);
border-bottom: 1px solid rgb(82, 38, 83);
font-weight: 900;
font-size: 24px;
background: transparent;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
padding: 0;
padding-left: 16px;
margin: 0;
color: white;
cursor: pointer;
`;
export const MenuScroll = styled.div`
height: calc(100vh - 102px);
overflow-y: auto;
`;
export const WorkspaceModal = styled.div`
padding: 10px 0 0;
& h2 {
padding-left: 20px;
}
& > button {
width: 100%;
height: 28px;
padding: 4px;
border: none;
background: transparent;
border-top: 1px solid rgb(28, 29, 28);
cursor: pointer;
&:last-of-type {
border-bottom: 1px solid rgb(28, 29, 28);
}
}
`;
export const Chats = styled.div`
flex: 1;
`;
export const AddButton = styled.button`
color: white;
font-size: 24px;
display: inline-block;
width: 40px;
height: 40px;
background: transparent;
border: none;
cursor: pointer;
`;
export const WorkspaceButton = styled.button`
display: inline-block;
width: 40px;
height: 40px;
border-radius: 10px;
background: white;
border: 3px solid #3f0e40;
margin-bottom: 15px;
font-size: 18px;
font-weight: 700;
color: black;
cursor: pointer;
`;
Workspace Styles (혹시나 해서 스타일까지 올렸습니다)
저기 흰색 부분 (Chats) 부분에 아무것도 출력이 되지 않습니다. 아예 DirectMessage 페이지 자체를 못 받아오는 것 같은데 route를 App으로 옮겨서 <div>TEST</div>만 출력 시킬 땐 작동이 됩니다..
그래서 <Channel />이랑 <DirectMessage />이렇게 Route바깥으로 빼서 출력해보면
const { data: userData } = useSWR(`/api/workspaces/${workspace}/users/${id}`, fetcher);
저 ${id} 부분이 undefined가 나옵니다..
메세지 클릭시 url 주소는 제대로 다 바뀌고 있습니다
답변을 작성해보세요.
0
TaeIl Lee
질문자2022.11.06
<Chats>
<Routes>
<Route path='/workspace/:workspace/channel/:channel' element={<Channel />} />
<Route path='/workspace/:workspace/dm/:id' element={<DirectMessage />} />
</Routes>
</Chats>
어제부터 해봤는데 해결이 안되네요..
윗글에 올린 workspace안에 저 Routes부분이 아예 안 불러 와지는 것 같습니다.
이 스크린샷처럼 아예 Chats 안의 컨텐츠가 로딩이 안됩니다.
import ChatBox from '@components/ChatBox';
import ChatList from '@components/ChatList';
import useInput from '@hooks/useInput';
import React, { useCallback } from 'react';
import { Container, Header } from './styles';
const Channel = () => {
const [chat, onChangeChat, setChat] = useInput('')
const onSubmitForm = useCallback((e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault
setChat('');
}, [])
return (
<Container>
<Header>채널</Header>
<ChatList />
<ChatBox chat={chat} onChangeChat={onChangeChat} onSubmitForm={onSubmitForm} />
</Container>
)
}
export default Channel;
Channel
const LogIn = loadable(() => import('@pages/LogIn'))
const SignUp = loadable(() => import('@pages/SignUp'));
const Workspace = loadable(() => import('@layouts/Workspace'));
const App = () => {
return (
<Routes >
<Route
path="/*"
element={<Navigate to="/login" replace />}
/>
<Route path='/login/*' element={<LogIn />} />
<Route path='/signup' element={<SignUp />} />
<Route path='/workspace/:workspace/*' element={<><Workspace /></>} />
</Routes>
)
}
export default App;
App
추가로 Channel이랑 App도 올리겠습니다.
Route랑 useParams도 한번 쭉 봤는데 안고쳐지네요..
<Channel />만 Routes밖으로 뺐을 때 나오는 undefined도 저 부분이 출력이 돼야 콘솔을 찍던 해서 고쳐 볼 수 있을텐데
지금까지 에러 메세지도 없어서 왜 안되는지 모르겠습니다..
답변 1