[Assignment 2] 미스터카멜 - 상품조회 이력과 필터링

이다은·2021년 7월 31일
2
post-thumbnail

🔗 Github

🔗 배포 링크

🔗 기능별 영상 링크

🔗 피그마 링크


👊 React Class Component로 개발하기

직접 구현한 부분

  • 전체 페이지 및 공통 컴포넌트 마크업 및 스타일링
  • 브랜드 목록 필터링 및 관심 없는 목록 필터링

브랜드 목록 필터링 : 전체 및 존재하는 브랜드 목록 필터링 기능 구현

(1) localStorage에 저장되어 있는 상품 목록 중 brand 값에 대해서 중복을 제거(Set 사용)하여 brand, brandFilter 값에 저장시킨다.

 componentDidMount() {
    const visitedItem = getProducts();
    this.setState({
      products: visitedItem,
    });

    const myStorageBrand = new Set(visitedItem.map((item) => item.brand));
    this.setState((prev) => ({
      ...prev,
      brand: [...myStorageBrand],
      brandFilter: [...myStorageBrand],
    }));
  }

(2) 브랜드 checkbox를 클릭하면, 넘겨져 온 값을 brandFilter에 저장한다.

setBrandFilter = (brandList) => {
    this.setState({
      brandFilter: brandList,
    });
};

(3) 브랜드를 checkox 형식으로 보여준다. 클릭하면 해당 브랜드 값을 e.target.checked 에 따라서 brandFilter값에 넣고/빼준다.

{brand.map((name, idx) => (
  <CheckBox
    key={`brand${idx}`}
    value={name}
    checked={filter?.includes(name)}
    onChange={(e) => {
              if (e.target.checked) {
                onChange([...filter, name]);
              } else {
                filter.length === brand.length
                  ? onChange([name])
                  : onChange(filter.filter((opt) => opt !== name));
              }
            }}
  />
))}                

(4) brandFilter값에 있는 브랜드만 보여주도록 한다.

products.filter((p) => brandFilter.includes(p.brand))

관심 없는 목록 필터링 : 관심 없는 상품 숨기기 체크박스 기능 구현

관심 없는 상품 숨기기를 클릭하면 아래 toggleDisLikeFilter를 실행하고 showDisLikeFilter값에 따라 필터링을 적용시킨다.

toggleDisLikeFilter = () => {
    this.setState((prev) => ({
      ...prev,
      showDisLikeFilter: !prev.showDisLikeFilter,
    }));
};

products.filter((p) => (showDisLikeFilter ? p.disLike === false : p))

팀원분들이 개발한 부분

다른 분들이 어떻게 코드를 개발하셨는지 궁금하여 분석 후 남겨봅니다.

다른 사람의 코드를 자주봐야 실력이 UP!!

[상품 목록 페이지]
🔻 public/data/data.json 파일 불러오기

  state = {
    allProducts: [],
  };

  async componentDidMount() {
    const productData = await getProductJsonData();
    const editedProductData = productData.map((item, idx) => {
      item.id = `prod${idx}`;
      item.disLike = 0;
      item.visitedDate = "";
      return item;
    });

    this.setState({
      allProducts: editedProductData,
    });
  }

  export const getProductJsonData = async () => {
    const response = await fetch("data/data.json");
    const data = await response.json();
    return data;
  };

[상품 상세 페이지]
❌ url에는 id값만 넘기면 된다. 아래의 방법은 좋지 않다고 하셨다. ❌
상품목록에서 아이템을 클릭하면 상세페이지로 이동하는데, 이때 state 값에 원하는 값을 넘겨 줄 수 있다.

  history.push({
      pathname: `/productdetail/${id}/${title}/${brand}/${price}/${disLike}`,
      state: { allProducts: allProducts },
  });

🔻 '랜덤 상품 조회' 버튼 클릭 시 현 상품 제외하고 랜덤 로드 기능 구현
: 상품아이디를 랜덤으로 가져와서, 중복되거나 관심없는 상품일 경우 다시 콜백

 handleRandomClick = () => {
    const { allProducts, product } = this.state;
    const randomNum = Math.floor(Math.random() * (allProducts.length - 1));
    const { title, brand, price, disLike } = allProducts[randomNum];

    if (`prod${randomNum}` === product.id || disLike) return () => this.handleRandomClick();

    history.push({
      pathname: `/productdetail/prod${randomNum}/${title}/${brand}/${price}/${disLike}`,
      state: { allProducts },
    });

    const productData = getProductData(this.path);
    this.setState({
      product: productData,
    });
  };

🔻 '관심 없음' 버튼 클릭 시 랜덤 로드 기능 구현
: 현재 상품을 가져와서 disLike값을 true로 변경

  handleDisLikeClick = () => {
    const products = getProducts();
    const currentData = products[products.length - 1];
    currentData.disLike = true;
    products.splice(products.length - 1, 1, currentData);
    setProducts(products);

    this.handleRandomClick();
  };

🔻 상품 상세 조회 시 이력 데이터 누적 기능 구현 & 동일 상품 조회 시 최신 데이터 갱신 기능 구현
: isExist로 확인 후 갱신 및 누적 기능 구현
: getProducts() localStorage.getItem() 함수
: setProducts() localStorage.setItem() 함수

 const products = getProducts();
 const currentItem = this.state.product;
 const isExist = products
      .map((product, index) => (product.id === currentItem?.id ? index : undefined))
      .filter((el) => (el !== undefined ? `${el}` : null));

 if (isExist.length > 0) products.splice(isExist[0], 1);
 const newData = products.concat(currentItem);
 setProducts(newData);

[상품 조회 이력 목록 페이지]
🔻 00시 기준 상품 조회 이력 및 관심 없는 상품 목록 초기화 기능 구현
: moment를 설치하여 현재시간이 "00:00:00"인 경우 clearProducts(localStorage 초기화를 실행하는 함수)를 실행한다.

  componentDidMount() {
    this.timerID = setInterval(() => this.tick(), 1000);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    const midnight = "00:00:00";
    let nowTime = null;

    nowTime = moment().format("HH:mm:ss");
    if (nowTime === midnight) {
      clearProducts();
      this.setState({
        products: [],
      });
    }
  }

🔻 정렬 기능: 최근 조회 순, 낮은 가격 순 팝업 기능 구현
: 최근 조회 순 버튼에 id = recent, 낮은 가격 순 버튼에 id = low 를 설정했다.
: 최근 조회 순 Date type 비교는 new Date(...).getTime() 형변환하여 비교한다.
: 낮은 가격 순 Number로 형변환하여 비교한다.

[참고] 오름차순 정렬 (1,2,3,4,...)

arr.sort(function(a, b)  {
 return a - b;
});

[참고] 내림차순 정렬 (...,4,3,2,1)

arr.sort(function(a, b)  {
 return b - a;
});
  handleSort = (e) => {
    const { id } = e.target;
    const { products } = this.state;
    const sortedState = products.sort((prev, next) =>
      id === "recent"
        ? new Date(next.visitedDate).getTime() - new Date(prev.visitedDate).getTime()
        : Number(prev.price) - Number(next.price)
    );

    this.setState((prev) => ({
      ...prev,
      products: sortedState,
    }));

    this.toggleSortOpen();
  };

🔻 '관심 없음' 상품 클릭 시 경고 메시지 모달 창 기능 구현

<Overlay isShow={isShow} onClick={() => isShowWarningPopup(false)}>
        <TextWrapper>
          <span>관심 없는 상품이므로 상세 페이지로 이동할 수 없습니다.</span>
        </TextWrapper>
</Overlay>
const Overlay = styled.div`
  position: fixed;
  display: flex;
  align-items: center;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 500px;
  height: 800px;
  margin: 10px auto;
  background-color: rgba(0, 0, 0, 0.4);
  visibility: ${({ isShow }) => (isShow ? "visible" : "hidden")};
  opacity: ${({ isShow }) => (isShow ? "1" : "0")};
  transition: 0.5s;
  z-index: 1;
`;

const TextWrapper = styled.div`
  ${({ theme }) => theme.flexSet("center", "center")}
  flex: 1;
  background-color: red;

  span {
    padding: 20px;
    color: white;
    text-align: "center";
  }
`;
profile
단단_프로트엔드개발자!

0개의 댓글