Web/React & Next.js

React | 카테고리 필터링, 동적 라우팅 기능

일렁이는코드 2021. 11. 14. 23:54

 

레이아웃과 제품 컴포넌트 작업을 끝내고 한 페이지에 sub_category 별로 직접 url 을 수정하여 제품을 띄우는 것은 성공시켰다. 하지만 카테고리를 클릭할때는 카테고리에 해당하는 제품들로 업데이트가 되어야하는데 이게 한페이지에서 어떻게 일어나는지에 대한 이해가 잘 가지 않았고 이 모든게 쿼리파라미터를 이용한 동적 라우팅 기능을 사용하면 해결할 수 있다는 걸 알았다. 


구현 기능 

카테고리를 클릭할 때마다 url 주소를 변경하고 그 url 주소를 가져와 컴포넌트를 변경한다. 

 

⭐Key Point

  • onClick 이벤트
  • history.push()
  • location.search()
  • componentDidUpdate

Output

Code

🥕 API Data 
${BASE_URL}/categories=1

 
더보기
{
    "results": {
        "category": "샤워",
        "category_id": 1,
        "count": 30,
        "subcategories": [
            {
                "count": 6,
                "name": "샴푸바",
                "sub_category_id": 1
            },
            {
                "count": 6,
                "name": "솝",
                "sub_category_id": 2
            },
            {
                "count": 6,
                "name": "샤워젤",
                "sub_category_id": 3
            },
            {
                "count": 6,
                "name": "배쓰밤",
                "sub_category_id": 4
            },
            {
                "count": 6,
                "name": "용품",
                "sub_category_id": 5
            }
        ]
    }
}

 

🥕 ListMenu.js (자식 컴포넌트)

 
더보기
import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { MENU_LIST } from './ListMenuSort';
import './ListMenu.scss';

export class ListMenu extends Component {
  changeValueToQuery = select => {
    const { location, history } = this.props;
    const paramsString = new URLSearchParams(location.search);

    if (select.target.value === 'sortBasic') {
      paramsString.delete('sort');
    } else if (select.target.value === 'sortHigh') {
      paramsString.delete('sort');
      paramsString.append('sort', '-price');
    } else if (select.target.value === 'sortLow') {
      paramsString.delete('sort');
      paramsString.append('sort', 'price');
    }
    history.push(`/productlist?${paramsString.toString()}`);
  };

  changeCategory = (value, id) => {
    const { history } = this.props;
    history.push(
      `/productlist?category=${
        value === 'Category' ? id : `1&sub_category=${id}`
      }`
    );
  };

  render() {
    const { menuCategory, menuSubCategory, listHeader } = this.props;
    const { changeValueToQuery } = this;

    return (
      <div className="listMenu">
        <div className="listTitle">
          <h2>{listHeader.name}</h2>
          <select defaultValue="sortBasic" onChange={changeValueToQuery}>
            {MENU_LIST.map(menu => {
              return (
                <option key={menu.id} value={menu.value}>
                  {menu.name}
                </option>
              );
            })}
          </select>
        </div>
        <ul className="listCategory">
          <li
            onClick={() =>
              this.changeCategory('Category', menuCategory.category_id)
            }
          >
            전체 ({menuCategory.count})
          </li>
          {menuSubCategory.map(menu => {
            return (
              <li
                key={menu.sub_category_id}
                onClick={() =>
                  this.changeCategory('SubCategory', menu.sub_category_id)
                }
              >
                {menu.name} ({menu.count})
              </li>
            );
          })}
        </ul>
      </div>
    );
  }
}

export default withRouter(ListMenu);

 

🥕 ProductList.js (부모 컴포넌트)

 

더보기
import React, { Component } from 'react';
import { API } from '../../config';
import ListHeader from './ListHeader/ListHeader';
import ListMenu from './ListMenu/ListMenu';
import Products from './Product/Product';
import './ProductList.scss';

export class ProductList extends Component {
  constructor() {
    super();
    this.state = {
      product: [],
      header: {},
      category: {},
      subCategory: [],
    };
  }

  fetchProductList(condition) {
    const { location } = this.props;
    fetch(`${API.productlist}${location.search || condition}`)
      .then(res => res.json())
      .then(data => {
        this.setState({
          product: data.results.data,
          header: data.results.products_header,
        });
      });
  }

  componentDidUpdate(prevProps) {
    const { location } = this.props;
    if (location.search !== prevProps.location.search) {
      this.fetchProductList(true);
    }
  }

  componentDidMount() {
    this.fetchProductList('?category=1');

    fetch(`${API.categories}/1`)
      .then(res => res.json())
      .then(data => {
        this.setState({
          category: data.results,
          subCategory: data.results.subcategories,
        });
      });
  }

  render() {
    const { product, header, category, subCategory } = this.state;

    return (
      <div className="productList">
        <ListHeader listHeader={header} />
        <ListMenu
          menuCategory={category}
          menuSubCategory={subCategory}
          listHeader={header}
        />
        <div className="products">
          <ul className="productSpace">
            {product.map(productInfo => {
              return <Products product={productInfo} key={productInfo.id} />;
            })}
          </ul>
        </div>
      </div>
    );
  }
}

export default ProductList;

 

반응형