studyHard
Published 2023. 5. 12. 13:20
[coz-shopping] Bookmark SEB_FE_44/과제

파일 구조

 

App.js 에서 Header.js, className = "main", Footer.js 가 있음.

 

className = "main" 에는 MainPage.js, ItemListPage.js, BookmarkPage.js 각각 경로설정.

 

MainPage.js 에서 최대 아이템4개를 렌더링해주는 MainListItems.js ,

북마킹된 아이템 최대 4개를 렌더링해주는 MainBookmarkItems.js 가 있음.

 

모든 아이템들은 Items.js 를 통해 렌더링.


App.js

더보기
import { useState } from "react";
import { Routes, Route } from "react-router-dom";
import Header from "./pages/Header";
import Footer from "./pages/Footer";
import MainPage from "./pages/MainPage";
import ItemListPage from "./pages/ItemListPage";
import BookmarkPage from "./pages/BookmarkPage";

import "./App.css";

function App() {
  const bookmarkRender = JSON.parse(localStorage.getItem("bookmark")); // 북마크 초기값
  const [bookmarkState, setBookmarkState] = useState(bookmarkRender); // 북마크 상태

  return (
    <div className="App">
      <Header />
      <main className="main">
        <Routes>
          <Route
            path="/"
            element={
              <MainPage
                bookmarkState={bookmarkState}
                setBookmarkState={setBookmarkState}
              />
            }
          />
          <Route
            path="/products"
            element={
              <ItemListPage
                bookmarkState={bookmarkState}
                setBookmarkState={setBookmarkState}
              />
            }
          />
          <Route path="/bookmark" element={<BookmarkPage />} />
        </Routes>
      </main>
      <Footer className="footer" />
    </div>
  );
}

export default App;

 

MainPage.js

더보기
import React, { useState, useEffect } from "react";

import MainListItems from "../components/items/MainListItems";
import MainBookmarkItems from "../components/items/MainBookmarkItems";
import axios from "axios";

import classes from "./MainPage.module.css";

const MainPage = ({ bookmarkState, setBookmarkState }) => {
  const [itemList, setItemList] = useState([]); // 아이템리스트 상태
  const url = "http://cozshopping.codestates-seb.link/api/v1/products?count=4";

  useEffect(() => {
    axios.get(url).then(res => {
      setItemList(res.data); // 받아온 데이터를 itemList 상태에 저장
    });
  }, []);

  return (
    <main>
      <h3 className={classes.mainText}>상품 리스트</h3>
      <MainListItems
        itemList={itemList}
        bookmarkState={bookmarkState}
        setBookmarkState={setBookmarkState}
      />
      <h3 className={classes.mainText}>북마크 리스트</h3>
      <MainBookmarkItems
        bookmarkState={bookmarkState}
        setBookmarkState={setBookmarkState}
      />
    </main>
  );
};

export default MainPage;

 

MainListItems.js

더보기
import React from "react";
import Item from "./Item";

import classes from "./MainListItems.module.css";

const MainListItems = ({ itemList, bookmarkState, setBookmarkState }) => {
  const handleIsBookmarked = item => {
    if (bookmarkState) {
      return bookmarkState.some(x => x.id === item.id);
    } else {
      return false;
    }
  };

  return (
    <ul className={classes.itemList}>
      {itemList.map(item => {
        return (
          <Item
            key={item.id}
            item={item}
            isBookmarked={handleIsBookmarked(item)}
            bookmarkState={bookmarkState}
            setBookmarkState={setBookmarkState}
          />
        );
      })}
    </ul>
  );
};

export default MainListItems;

 

MainBookmarktems.js

더보기
import Item from "./Item";
import Error from "./Error";

import classes from "./MainListItems.module.css";

const MainBookmarkItems = ({ bookmarkState, setBookmarkState }) => {
  const handleIsBookmarked = item => {
	// 북마크 상태 체크 함수
    if (bookmarkState) {
      return bookmarkState.some(x => x.id === item.id);
    } else {
      return false;
    }
  };

  return (
    <ul className={classes.itemList}>
      {bookmarkState && bookmarkState.length !== 0 ? ( // 에러 방지
        bookmarkState.slice(0, 4).map(item => {
          return (
            <Item
              key={item.id}
              item={item}
              isBookmarked={handleIsBookmarked(item)}
              bookmarkState={bookmarkState}
              setBookmarkState={setBookmarkState}
            />
          );
        })
      ) : (
        <Error /> // bookmarkState에 데이터가 없다면 Error 컴포넌트 렌더링
      )}
    </ul>
  );
};

export default MainBookmarkItems;

 

Item.js

더보기
import React from "react";

import classes from "./MainListItems.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faStar } from "@fortawesome/free-solid-svg-icons";

const Item = ({ item, setBookmarkState, isBookmarked }) => {
  const handleBookmark = item => {
	// bookmark에 로컬스토리지 "bookmark"를 가져오고 없으면 빈배열 생성
    const bookmark = JSON.parse(localStorage.getItem("bookmark")) || [];
	// bookmark에 item이 있나 확인 후, 있으면 해당 index를 없으면 -1을
	// existingItemIndex에 할당
    const existingItemIndex = bookmark.findIndex(x => x.id === item.id);
    const isExistingItem = existingItemIndex !== -1;

    if (isExistingItem) {
	// bookmark에 있으면 삭제, 없으면 배열 맨앞에 추가
      bookmark.splice(existingItemIndex, 1);
    } else {
      bookmark.unshift(item);
    }

	// 로컬스토리지에 bookmark 덮어쓰기, State도 변경
    localStorage.setItem("bookmark", JSON.stringify(bookmark));
    setBookmarkState(JSON.parse(localStorage.getItem("bookmark")));
  };

  return (
    <div className={classes.item}>
      <div className={classes.imgBox}>
        <img
          className={classes.img}
          src={item.image_url ? item.image_url : item.brand_image_url}
          alt="img"
        />
        <FontAwesomeIcon
          className={isBookmarked ? classes.bookcolor : classes.bookmark}
          size="lg"
          icon={faStar}
          onClick={() => {
            handleBookmark(item);
          }}
        />
      </div>
      <div className={classes.firstLine}>
        {
          <span className={classes.title}>
            {item.title ? item.title : item.brand_name}
          </span>
        }
        {(() => {
          switch (item.type) {
            case "Brand":
              return <span className={classes.customer}>관심고객수</span>;
            case "Product":
              return (
                <span className={classes.percent}>
                  {item.discountPercentage}%
                </span>
              );
            default:
              return "";
          }
        })()}
      </div>
      <div className={classes.firstLine}>
        <span>{item.sub_title ? item.sub_title : ""}</span>
        <span className={classes.follower}>
          {(() => {
            switch (item.type) {
              case "Product":
                return `${item.price
                  .toString()
                  .replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",")}원`;
              case "Brand":
                return item.follower
                  .toString()
                  .replace(/\B(?<!\.\d*)(?=(\d{3})+(?!\d))/g, ",");
              default:
                return "";
            }
          })()}
        </span>
      </div>
    </div>
  );
};

export default Item;

 

'SEB_FE_44 > 과제' 카테고리의 다른 글

[coz-shopping] 무한 스크롤  (0) 2023.05.16
[coz-shopping] Modal  (0) 2023.05.15
Section 3 회고  (4) 2023.05.09
섹션3 기술면접 정리  (0) 2023.05.09
Section 2 회고  (1) 2023.04.10
profile

studyHard

@언젠간코딩잘함

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!