-
Notifications
You must be signed in to change notification settings - Fork 35
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
The head ref may contain hidden characters: "Next-\uAE40\uAC15\uC6B0-sprint11"
[김강우] sprint11 #312
Changes from all commits
18119bb
a94153d
ad003e0
47d08c9
fb57ba0
b9c5cd0
86e6cc5
7f99589
dcb939a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,6 +33,8 @@ | |
{ | ||
"labelAttributes": ["htmlFor"] | ||
} | ||
] | ||
], | ||
"consistent-return": "off", | ||
"react/require-default-props": "off" | ||
} | ||
} |
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) { | ||
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; |
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]); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. p3; 제 생각에 fetchArticles를 할 때, 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 ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
p4.
e.target.files 가 없는 경우 함수를 종료해버리는 것도 괜찮을 것 같아요!