-
Notifications
You must be signed in to change notification settings - Fork 38
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
[염정훈] Sprint6 #176
The head ref may contain hidden characters: "React-\uC5FC\uC815\uD6C8-sprint6"
[염정훈] Sprint6 #176
Changes from all commits
81a6f19
2cfb3fe
fe91762
4db8289
c9f0afa
b845ff4
3cb7fec
5f89c88
129fb90
29f7f03
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 |
---|---|---|
|
@@ -4,6 +4,7 @@ | |
/node_modules | ||
/.pnp | ||
.pnp.js | ||
.env | ||
|
||
# testing | ||
/coverage | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,7 @@ | ||
export async function getProducts({ pageSize, order }) { | ||
const query = `pageSize=${pageSize}&orderBy=${order}`; | ||
const response = await fetch( | ||
`https://panda-market-api.vercel.app/products?page=1&${query}` | ||
); | ||
export async function getProducts({ page, pageSize, order }) { | ||
const apiUrl = process.env.REACT_APP_API_URL; | ||
const query = `page=${page}&pageSize=${pageSize}&orderBy=${order}`; | ||
const response = await fetch(`${apiUrl}.vercel.app/products?${query}`); | ||
const body = await response.json(); | ||
return body; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import { useEffect, useRef, useState } from "react"; | ||
|
||
function AddItem() { | ||
const [formData, setFormDate] = useState({ | ||
name: "", | ||
info: "", | ||
price: "", | ||
tag: "", | ||
}); | ||
Comment on lines
+4
to
+9
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. 폼으로 관련이 있는 상태끼리 객체로 관리 하시는 점 좋네요. |
||
const [addItemBtn, setAddItemBtn] = useState(""); | ||
const [btnDisabled, setBtnDisabled] = useState(true); | ||
const [inputFileUrl, setInputFileUrl] = useState(null); | ||
const inputFileImg = useRef(null); | ||
const imgAccept = ".jpg, .jpeg, .png"; | ||
|
||
const inputChange = (e) => { | ||
// input value 상태 변경 | ||
const { name, value } = e.target; | ||
setFormDate({ | ||
...formData, | ||
[name]: value, | ||
}); | ||
}; | ||
|
||
const inputFileChange = () => { | ||
const inputFile = inputFileImg.current.files[0]; | ||
console.log(inputFile); | ||
|
||
if (inputFile) { | ||
const fileUrl = URL.createObjectURL(inputFile); | ||
setInputFileUrl(fileUrl); | ||
} | ||
}; | ||
|
||
const addImgDelete = () => { | ||
setInputFileUrl(null); | ||
}; | ||
|
||
const validAddItem = () => { | ||
// 유효성 검사 | ||
if ( | ||
formData.name !== "" && | ||
formData.info !== "" && | ||
formData.price !== "" && | ||
formData.tag !== "" | ||
) { | ||
setAddItemBtn("on"); | ||
setBtnDisabled(false); | ||
} else { | ||
setAddItemBtn(""); | ||
setBtnDisabled(true); | ||
} | ||
}; | ||
Comment on lines
+39
to
+53
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. 현재 방식과 큰 차이는 없지만, 조금 더 역할을 구분해주시는 방법도 추천드립니다. 예를 들어서 유효성 검사하는 함수 내부에서는
이 로직에 대한 결과만 반환하고 useEffect 내부에서 이 함수의 결과를 보고 setState가 일어나는 방식으로요! |
||
|
||
useEffect(() => { | ||
// input 데이터 변경 될때마다 유효성 검사 실행 | ||
validAddItem(); | ||
}, [formData]); | ||
Comment on lines
+55
to
+58
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. 상태에 따라서 매번 유효성 검사해주시는 로직을 useEffect를 활용해서 잘 작성해주셨네요! |
||
|
||
return ( | ||
<section className="add-item-wrap max-wrap"> | ||
<form method="post"> | ||
<div className="add-item-title"> | ||
<h2>상품 등록하기</h2> | ||
<button type="button" disabled={btnDisabled} className={addItemBtn}> | ||
등록 | ||
</button> | ||
</div> | ||
<div className="add-input"> | ||
<h3 className="add-input-title">상품 이미지</h3> | ||
<div className="add-img-list"> | ||
<label htmlFor="add-input-img" className="add-img-area"> | ||
<img src="/images/i-plus.png" alt="플러스 아이콘" /> | ||
이미지 등록 | ||
</label> | ||
<input | ||
id="add-input-img" | ||
type="file" | ||
ref={inputFileImg} | ||
accept={imgAccept} | ||
onChange={inputFileChange} | ||
/> | ||
{inputFileUrl ? ( | ||
<div className="add-img-item"> | ||
<img src={inputFileUrl} alt="상품 등록 이미지" /> | ||
<button type="button" onClick={addImgDelete}></button> | ||
</div> | ||
) : ( | ||
"" | ||
)} | ||
</div> | ||
</div> | ||
<div className="add-input"> | ||
<label htmlFor="add-input-name" className="add-input-title"> | ||
상풍명 | ||
</label> | ||
<input | ||
id="add-input-name" | ||
type="text" | ||
name="name" | ||
placeholder="상품명을 입력해주세요." | ||
value={formData.name} | ||
onChange={inputChange} | ||
/> | ||
</div> | ||
<div className="add-input"> | ||
<label htmlFor="add-input-info" className="add-input-title"> | ||
상품 소개 | ||
</label> | ||
<textarea | ||
id="add-input-info" | ||
name="info" | ||
placeholder="상품 소개를 입력해주세요." | ||
value={formData.info} | ||
onChange={inputChange} | ||
></textarea> | ||
</div> | ||
<div className="add-input"> | ||
<label htmlFor="add-input-price" className="add-input-title"> | ||
판매가격 | ||
</label> | ||
<input | ||
id="add-input-price" | ||
type="number" | ||
name="price" | ||
placeholder="판매 가격을 입력해주세요." | ||
value={formData.price === NaN ? null : formData.price} | ||
onChange={inputChange} | ||
/> | ||
</div> | ||
<div className="add-input"> | ||
<label htmlFor="add-input-tag" className="add-input-title"> | ||
태그 | ||
</label> | ||
<input | ||
id="add-input-tag" | ||
type="text" | ||
name="tag" | ||
placeholder="태그를 입력해주세요." | ||
value={formData.tag} | ||
onChange={inputChange} | ||
/> | ||
<ul className="tag-list"> | ||
<li> | ||
티셔츠 <button type="button"></button> | ||
</li> | ||
<li> | ||
상의 <button type="button"></button> | ||
</li> | ||
</ul> | ||
</div> | ||
</form> | ||
</section> | ||
); | ||
} | ||
|
||
export default AddItem; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { Link } from "react-router-dom"; | ||
import { getProducts } from "../api.js"; | ||
import { useEffect, useState } from "react"; | ||
|
||
|
@@ -15,50 +16,70 @@ const responsivePageSize = () => { | |
} | ||
}; | ||
|
||
const recent = "recent"; | ||
const favorite = "favorite"; | ||
Comment on lines
+19
to
+20
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. 보통 상수로 관리할때 상수라는 점을 표현하기 위해 변수명으로 대문자를 많이 사용합니다. RECENT 이런식으로요! |
||
|
||
function AllProduct() { | ||
const [products, setProducts] = useState([]); | ||
const [page, setPage] = useState(1); | ||
const [pageSize, setPageSize] = useState(10); | ||
const [order, setOrder] = useState("recent"); | ||
const [order, setOrder] = useState(recent); | ||
const [dropArrow, setDropArrow] = useState(""); | ||
const [dropDisplay, setDropDisplay] = useState("none"); | ||
const [orderTxt, setOrderTxt] = useState("최신순"); | ||
const [pagesNum, setPagesNum] = useState([1, 2]); | ||
|
||
const handleLoad = async (options) => { | ||
let { list } = await getProducts(options); | ||
setProducts(list); | ||
// console.log(list); | ||
}; | ||
|
||
const handleDropClick = () => { | ||
dropArrow === "" ? setDropArrow("on") : setDropArrow(""); | ||
dropDisplay === "none" ? setDropDisplay("block") : setDropDisplay("none"); | ||
setDropArrow(dropArrow === "" ? "on" : ""); | ||
setDropDisplay(dropDisplay === "none" ? "block" : "none"); | ||
}; | ||
|
||
const handleNewsOrder = (e) => { | ||
const menuTxt = e.target.textContent; | ||
setOrderTxt(menuTxt); | ||
setDropArrow(""); | ||
setDropDisplay("none"); | ||
setOrder("recent"); | ||
setOrder(recent); | ||
}; | ||
|
||
const handleBestOrder = (e) => { | ||
const menuTxt = e.target.textContent; | ||
setOrderTxt(menuTxt); | ||
setDropArrow(""); | ||
setDropDisplay("none"); | ||
setOrder("favorite"); | ||
setOrder(favorite); | ||
}; | ||
|
||
const pageNumClick = (page) => { | ||
setPage(page); | ||
}; | ||
|
||
const prevPageBtn = () => { | ||
if (page !== 1) { | ||
setPage(page - 1); | ||
} | ||
}; | ||
|
||
const nextPageBtn = () => { | ||
if (page !== pagesNum.length) { | ||
setPage(page + 1); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
handleLoad({ pageSize, order }); | ||
handleLoad({ page, pageSize, order }); | ||
|
||
const handleResize = () => { | ||
setPageSize(responsivePageSize()); | ||
}; | ||
|
||
window.addEventListener("resize", handleResize); | ||
}, [order, pageSize]); | ||
}, [page, order, pageSize]); | ||
|
||
return ( | ||
<> | ||
|
@@ -67,7 +88,9 @@ function AllProduct() { | |
<h2>판매중인 상품</h2> | ||
<div className="product-info-menu"> | ||
<input type="search" placeholder="검색할 상품을 입력해주세요." /> | ||
<button type="button">상품 등록하기</button> | ||
<Link to="/AddItem" className="item-add-btn"> | ||
상품 등록하기 | ||
</Link> | ||
<div className="drop-menu"> | ||
<p className={dropArrow} onClick={handleDropClick}> | ||
{orderTxt} | ||
|
@@ -118,27 +141,26 @@ function AllProduct() { | |
<div className="pageNav"> | ||
<ul> | ||
<li> | ||
<button type="button"> | ||
<button type="button" onClick={prevPageBtn}> | ||
<img src="/images/i-arrow-left.png" alt="왼쪽 화살표" /> | ||
</button> | ||
</li> | ||
<li className="on"> | ||
<button type="button">1</button> | ||
</li> | ||
<li> | ||
<button type="button">2</button> | ||
</li> | ||
<li> | ||
<button type="button">3</button> | ||
</li> | ||
<li> | ||
<button type="button">4</button> | ||
</li> | ||
<li> | ||
<button type="button">5</button> | ||
</li> | ||
{pagesNum.map((pageNum) => { | ||
return ( | ||
<li key={pageNum} className={pageNum === page ? "on" : ""}> | ||
<button | ||
type="button" | ||
onClick={() => { | ||
pageNumClick(pageNum); | ||
}} | ||
> | ||
{pageNum} | ||
</button> | ||
</li> | ||
); | ||
})} | ||
<li> | ||
<button type="button"> | ||
<button type="button" onClick={nextPageBtn}> | ||
<img src="/images/i-arrow-right.png" alt="왼쪽 화살표" /> | ||
</button> | ||
</li> | ||
|
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.
닷엔브 활용해주신거 좋네요. 추가로 보통 baseURL이라는 이름도 많이 사용합니다!