Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[김강우] sprint11 #312

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
{
"labelAttributes": ["htmlFor"]
}
]
],
"consistent-return": "off",
"react/require-default-props": "off"
}
}
54 changes: 27 additions & 27 deletions components/BestBoards.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,51 @@
import getArticles, { Article } from '@/pages/api/client';
import { useCallback, useContext, useEffect, useState } from 'react';
import { useContext, useEffect, useState } from 'react';
import { DeviceContext } from '@/contexts/DeviceContext';
import getArticles from '@/lib/api/getArticles';
import Link from 'next/link';
import { Article } from '@/types/Article';
import BestMedal from './boards/BestMedal';
import BestContent from './boards/BestContent';
import BestInfo from './boards/BestInfo';

const pageSizeMap = {
mobile: 1,
tablet: 2,
desktop: 3,
};

function BestBoards() {
const device = useContext(DeviceContext);
const [boards, setBoards] = useState<Article[]>([]);

const pageSizeByDevice = useCallback(() => {
if (device === 'mobile') return 1;
if (device === 'tablet') return 2;
return 3;
}, [device]);

useEffect(() => {
const handleLoad = async () => {
const { list } = await getArticles({
page: 1,
pageSize: pageSizeByDevice(),
pageSize: pageSizeMap[device],
orderBy: 'like',
});
setBoards(() => list);
setBoards(list);
};
handleLoad();
}, [pageSizeByDevice]);

if (!boards) return null;
}, [device]);

return (
<div className="mx-auto mt-[70px] max-w-[343px] pt-[16px] tablet:max-w-[696px] desktop:max-w-[1200px]">
<div className="mx-auto mt-[70px] max-w-[343px] pt-4 tablet:max-w-[696px] desktop:max-w-[1200px]">
<h1 className="font-xl-20px-bold">베스트 게시글</h1>
<div className="mb-[10px] mt-[16px] flex gap-[16px]">
{boards &&
boards.map(board => (
<div
key={board.id}
className="w-full rounded-[8px] bg-secondary-50"
>
<div className="px-[24px] pb-[16px]">
<BestMedal />
<BestContent board={board} />
<BestInfo board={board} />
</div>
<div className="mb-[10px] mt-4 flex gap-4">
{boards.map(board => (
<Link
href={`/board/${board.id}`}
key={board.id}
className="w-full rounded-[8px] bg-secondary-50"
>
<div className="px-6 pb-4">
<BestMedal />
<BestContent board={board} />
<BestInfo board={board} />
</div>
))}
</Link>
))}
</div>
</div>
);
Expand Down
76 changes: 76 additions & 0 deletions components/FileInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Image from 'next/image';
import { ChangeEvent, useEffect, useRef, useState } from 'react';

type Props = {
value: Blob | MediaSource | null;
name: string;
onChange: (name: string, file: File | null) => void;
};

function FileInput({ value, name, onChange }: Props) {
const [preview, setPreview] = useState('');
const fileInputRef = useRef<HTMLInputElement>(null);

const handleFileInputChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p4.
e.target.files 가 없는 경우 함수를 종료해버리는 것도 괜찮을 것 같아요!

  const handleFileInputChange = (e: ChangeEvent<HTMLInputElement>) => {
    if (!e.target.files) return;

    const nextFile = e.target.files[0];
    onChange(name, nextFile);
    }
  };

const nextFile = e.target.files[0];
onChange(name, nextFile);
}
};

const handleFileInputCancel = () => {
onChange(name, null);
};

useEffect(() => {
if (!value) return;

const nextPreview = URL.createObjectURL(value);
setPreview(nextPreview);

return () => {
setPreview('');
URL.revokeObjectURL(nextPreview);
};
}, [value]);

return (
<div className="flex gap-[10px]">
<label
htmlFor="image"
className="flex h-[168px] w-[168px] cursor-pointer flex-col items-center justify-center rounded-[8px] bg-secondary-100 py-[6px] placeholder:font-lg-16px-regular desktop:h-[282px] desktop:w-[282px]"
>
<Image
width={48}
height={48}
src="/icon/plus.png"
alt="file input button"
/>
<p className="text-secondary-400 font-lg-16px-regular">이미지 등록</p>
</label>
{preview && (
<div className="relative h-[168px] w-[168px] desktop:h-[282px] desktop:w-[282px]">
<Image className="bg-cover" fill src={preview} alt="preview" />
<Image
className="absolute right-3 top-3"
width={22}
height={24}
onClick={handleFileInputCancel}
src="/icon/blue_X.png"
alt="cancel"
/>
</div>
)}
<input
className="hidden"
ref={fileInputRef}
id="image"
name="image"
type="file"
onChange={handleFileInputChange}
/>
</div>
);
}

export default FileInput;
61 changes: 45 additions & 16 deletions components/Header/index.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,56 @@
import { PropsWithChildren } from 'react';
import { PropsWithChildren, useContext, useEffect, useState } from 'react';
import Image from 'next/image';
import VerticalDivider from '@/components/elements/VerticalDivider';
import Link from 'next/link';
import { DeviceContext } from '@/contexts/DeviceContext';
import { useAuth } from '@/contexts/AuthProvider';
import Button from '../elements/button/Button';

function Header({ children }: PropsWithChildren) {
const { user } = useAuth();
const [mount, setMount] = useState<boolean>(false);
const device = useContext(DeviceContext);

useEffect(() => {
setMount(true);
}, []);

return (
<header className="fixed left-0 right-0 top-0 z-50 bg-white">
<div className="px-[16px] py-[15px]">
<div className="mx-auto flex max-w-[1200px] items-center justify-between gap-[8px]">
<Image
width={81}
height={40}
src="/logo/logo_mobile.png"
alt="logo"
/>
<div className="flex grow gap-[8px] font-lg-16px-bold">
<div className="px-[16px] py-[15px] tablet:py-[10px]">
<div className="mx-auto flex max-w-[1200px] items-center justify-between gap-[8px] tablet:gap-[20px]">
<Link href="/">
{mount && device === 'mobile' ? (
<Image
width={81}
height={40}
src="/logo/logo_mobile.png"
alt="logo"
/>
) : (
<Image
width={153}
height={51}
src="/logo/logo_not_mobile.png"
alt="logo"
/>
)}
</Link>
<div className="flex grow gap-[8px] font-lg-16px-bold tablet:gap-[20px]">
{children}
</div>
<Image
width={40}
height={40}
src="/initial_profile.png"
alt="profile"
/>
{mount && user ? (
<Image
width={40}
height={40}
src="/initial_profile.png"
alt="profile"
/>
) : (
<Link href="/login">
<Button disabled={false}>로그인</Button>
</Link>
)}
</div>
</div>
<VerticalDivider />
Expand Down
58 changes: 30 additions & 28 deletions components/MainBoards.tsx
Original file line number Diff line number Diff line change
@@ -1,63 +1,65 @@
import getArticles, { Article, ArticlesQuery } from '@/pages/api/client';
import React, { useCallback, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import Link from 'next/link';
import { Article, GetArticlesQuery } from '@/types/Article';
import getArticles from '@/lib/api/getArticles';
import RecentContent from './boards/MainContent';
import RecentInfo from './boards/MainInfo';
import VerticalDivider from './elements/VerticalDivider';
import BoardTitle from './boards/BoardTitle';

function MainBoards() {
const router = useRouter();
const [boards, setBoards] = useState<Article[]>([]);
const [keyword, setKeyword] = useState<string>('');
const [orderBy, setOrderBy] = useState<ArticlesQuery['orderBy']>('recent');
const [isLoading, setIsLoading] = useState<boolean>(true);
const [orderBy, setOrderBy] = useState<GetArticlesQuery['orderBy']>('recent');
const [isLoading, setIsLoading] = useState<boolean>(false);

const handleLoad = useCallback(async () => {
const fetchArticles = useCallback(async () => {
const query = {
page: 1,
pageSize: 6,
orderBy,
keyword,
};
const { list } = await getArticles(query);
router.push({
query,
});
setBoards(() => list);
}, [keyword, orderBy, router]);

useEffect(() => {
if (isLoading === true) {
handleLoad();
setIsLoading(() => false);
if (isLoading === false) {
setIsLoading(true);
const { list } = await getArticles(query);
setIsLoading(false);
setBoards(list);
}
}, [isLoading, keyword, orderBy, handleLoad]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [keyword, orderBy]);

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

p3;
deps에서 isLoading 상태를 제거한 이유가 있으신가요?

제 생각에 fetchArticles를 할 때,
loading 상태를 true로 변경시켰다가
fetch 후 loading 상태를 false로 다시 변경시키는 것은 조건에 상관 없이 일어나야 맞는 것 같습니다.

  const fetchArticles = useCallback(async () => {
    const query = {
      page: 1,
      pageSize: 6,
      orderBy,
      keyword,
    };
    setIsLoading(true);
    const { list } = await getArticles(query);

    setIsLoading(false);
    setBoards(list);
  }, [keyword, orderBy]);

if (!boards) return null;
useEffect(() => {
fetchArticles();
}, [fetchArticles]);

return (
<div className="mx-auto mt-[70px] max-w-[343px] pt-[16px] tablet:max-w-[696px] desktop:max-w-[1200px]">
<div className="mx-auto mt-[70px] max-w-[343px] pt-4 tablet:max-w-[696px] desktop:max-w-[1200px]">
<BoardTitle
keyword={keyword}
orderBy={orderBy}
onChangeKeyword={setKeyword}
onChangeOrderBy={setOrderBy}
setIsLoading={setIsLoading}
/>
<div className="mb-[10px] mt-[16px]">
{boards &&
boards.map((board, i) => (
<div className="mb-[10px] mt-4">
{boards.map((board, i) => {
const isLastArticle = i !== boards.length - 1;
return (
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이렇게 변수화하니까 더 알아보기 좋은 것 같아요👍

<>
<div key={board.id} className="w-full rounded-[8px] bg-[#FCFCFC]">
<Link
href={`/board/${board.id}`}
key={board.id}
className="w-full rounded-[8px] bg-[#FCFCFC]"
>
<div className="mt-[24px] pb-[24px]">
<RecentContent board={board} />
<RecentInfo board={board} />
</div>
</div>
{i !== boards.length - 1 && <VerticalDivider />}
</Link>
{isLastArticle && <VerticalDivider />}
</>
))}
);
})}
</div>
</div>
);
Expand Down
Loading
Loading