React-Hooks를 사용하여 필터 버튼 구현하기 -1-

Nanotube·2021년 6월 7일
1

React

목록 보기
3/7

React-Hooks를 활용하여 버튼 클릭시 데이터를 필터링 해주는 웹 페이지를 구현하려고합니다.

작은 미니 프로젝트로 그래픽카드 정보를 담는 페이지입니다.

React App 생성하기

npx create-react-app filter-app으로 리액트 앱을 생성해 줍니다.

생성된 React App에서 불필요한 파일들을 정리 해 줍니다.

Index

JS

import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import Display from "./Display";

ReactDOM.render(
  <React.StrictMode>
    <Display />
  </React.StrictMode>,
  document.getElementById("root")
);

CSS

body {
  box-sizing: border-box;
  margin: 0;
  width: 100%;
  height: 100vh;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
    "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
    sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

Dipslay

JS

import { useState } from "react";
import Card from "./components/Card";
import "./display.css";

import items from "./dummy.json";

export default function Display() {
  const categories = ["All", ...new Set(items.map((item) => item.company))];

  console.log(categories);
  const [activeCat, setActiveCat] = useState(categories);
  const [data, setData] = useState(items);

  const activeCategory = (btn) => {
    if (btn === "All") {
      setData(items);
      return;
    }

    const filteredData = items.filter((item) => item.company === btn);
    setData(filteredData);
  };

  return (
    <main>
      <header>
        <h1>그래픽 카드</h1>
      </header>
      <section>
        <article className="categories">
          {activeCat.map((cate) => {
            return (
              <button
                className="cat_btn hover"
                onClick={() => activeCategory(cate)}
              >
                {cate}
              </button>
            );
          })}
        </article>
        <article className="card_list">
          {data.map((g, i) => {
            return (
              <div className="card_container">
                <Card card={g} key={i} />
              </div>
            );
          })}
        </article>
      </section>
      <footer></footer>
    </main>
  );
}

CSS

main {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

main > section {
  display: flex;
  flex-direction: column;
}

.categories {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
}

.cat_btn {
  width: 170px;
  height: 50px;
  margin: 0 20px;

  color: white;
  cursor: pointer;

  font-family: "Noto Sans", sans-serif;
  font-size: 2rem;
  font-weight: bold;

  letter-spacing: 1px;

  box-shadow: -1px 3px 3px 0 rgba(80, 80, 80, 0.698);

  border: none;
  border-radius: 5px;
}

.hover {
  background: rgba(54, 54, 54, 0.424);
  transition: all 0.3s ease;
}
.hover:hover {
  width: 180px;
  background: rgba(11, 195, 11, 0.74);
  box-shadow: -1px 6px 10px 0 rgba(54, 54, 54, 0.424);
}
.hover:visited {
  width: 180px;
  background: rgba(11, 195, 11, 0.74);
  box-shadow: -1px 6px 10px 0 rgba(54, 54, 54, 0.424);
}

.card_list {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
}

Card

JS

import { specialChars } from "@testing-library/user-event";
import "./card.css";

export default function Card({ card }) {
  // console.log(card);
  const { model, kind, company, image, spec } = card;
  console.log(spec.architecture);
  return (
    <>
      <header className="card_header">
        <h1>{model}</h1>
      </header>
      <figure className="image_box">
        <img src={image} />
      </figure>
      <div className="card_info">
        <span>제조사: {company}</span>
        <span>종류: {kind}</span>
        <span>모델명: {model}</span>
        <span>아키텍쳐: {spec.architecture}</span>
        <span>메모리: {spec.vram}</span>
      </div>
    </>
  );
}

CSS

.card_container {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* border: 1px black solid; */
  width: 20%;

  margin: 3% 1%;
  padding: 1%;

  box-shadow: -1px 6px 10px 0 rgba(80, 80, 80, 0.473);
}

.card_header {
  font-size: 2em;
}

.image_box {
  width: 100%;
}

.image_box img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.card_info {
  width: 100%;
  padding-left: 20%;

  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  align-items: flex-start;

  font-size: 1.1em;
}

필터 버튼 다른 방식으로 구현

위에서처럼 단순히 map을 사용하여 Button을 나열했다면 이번 방법은 UseEffect를 활용한 다른 방식으로 작성됬습니다.

이번에는 버튼을 눌렀을때, 어느 버튼을 눌렀는지 알기위해 버튼효과를 유지시키기 위한 방법도 들어가있습니다.

[수정 후]Display

JS


  const [activeCat, setActiveCat] = useState("All");
  const [data, setData] = useState(items);

  // const activeCategory = (btn) => {
  //   if (btn === "All") {
  //     setData(items);
  //     return;
  //   }

  //   const filteredData = items.filter((item) => item.company === btn);
  //   setData(filteredData);
  // };

  useEffect(() => {
    activeCat === "All"
      ? setData(items)
      : setData(items.filter((vga) => vga.company === activeCat));
  }, [activeCat]);

  return (
    <main>
      <header>
        <h1>그래픽 카드</h1>
      </header>
      <section>
        <article className="categories">
          {/* {activeCat.map((cate) => {
            return <button className="cat_btn hover">{cate}</button>;
          })} */}
          {/* {activeCat.map((cat) => {
            <Card name={cat} />;
          })} */}
          <Catbtn
            name="All"
            catActive={activeCat === "All" ? true : false}
            handleSetCat={setActiveCat}
          />
          <Catbtn
            name="Nvidia"
            catActive={activeCat === "Nvidia" ? true : false}
            handleSetCat={setActiveCat}
          />
          <Catbtn
            name="AMD"
            catActive={activeCat === "AMD" ? true : false}
            handleSetCat={setActiveCat}
          />
        </article>

activeCat 의 기본상태는 "All"입니다. 웹페이지 렌더링시 전체 제품을 보여줍니다.

아래의 useEffect를 사용해줘 각 버튼을 누를때마다 activeCat의 상태가 변경됩니다.
: setData(items.filter((vga) => vga.company === activeCat));
이부분은 Json형태로 작성된 더미파일에서 객체의 값이 선택한 버튼의 이름과 같다면 해당 제품을 제외한 다른 제품들은 필터링 됩니다.

css

main {
  width: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

main > section {
  display: flex;
  flex-direction: column;
}

.categories {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
}

.card_list {
  display: flex;
  justify-content: center;
  flex-wrap: wrap;
}

[수정 후]Catbtn

js


import "./catbtn.css";

export default function Catbtn({ name, catActive, handleSetCat }) {
  return (
    <button
      className={`cat_btn hover ${catActive ? "active_btn" : null}`}
      onClick={() => handleSetCat(name)}
    >
      {name}
    </button>
  );
}

Catbtn은 상위 컴포넌트 Display에서 전달받은 props를 활용해줍니다.

<button> 태그에서 className의 속성을 보시면 선택된 버튼에 따라 CSS 스타일링 효과를 줄 수 있습니다.

css

.cat_btn {
  width: 170px;
  height: 50px;
  margin: 0 20px;

  color: white;
  cursor: pointer;

  font-family: "Noto Sans", sans-serif;
  font-size: 2rem;
  font-weight: bold;

  letter-spacing: 1px;

  box-shadow: -1px 3px 3px 0 rgba(80, 80, 80, 0.698);

  border: none;
  border-radius: 5px;
}

.hover {
  background: rgba(54, 54, 54, 0.424);
  transition: all 0.3s ease;
}
.hover:hover {
  width: 180px;
  background: rgba(11, 195, 11, 0.74);
  box-shadow: -1px 6px 10px 0 rgba(54, 54, 54, 0.424);
}
.active_btn:active,
.active_btn:focus {
  width: 180px;
  background: rgba(11, 195, 11, 0.74);
  box-shadow: -1px 6px 10px 0 rgba(54, 54, 54, 0.424);
}

깃허브 주소 https://github.com/hsh411/filter-components

profile
나노튜브

0개의 댓글