[React]조건별로 검색하는 검색창 구현하기

Hyoyoung Kim·2023년 4월 4일
2

🤔 프로젝트를 진행하면서 조건별로 검색창을 구현해야했다.

드롭박스에서 '상품명'을 선택하면 검색창에 검색한 검색어랑 같은 상품명 데이터가 검색되어야 하고
드롭박스에서 '카테고리명'을 선택하면 검색창에 검색한 검색어랑 같은 카테고리만 검색되어야 한다.

서버에 request으로 보내야 하는 것들

import * as S from './style';
import { useNavigate } from 'react-router-dom';
import { useEffect } from 'react';
import { Searchcompo2 } from '../../often/Searchcompo2';
import { useState } from 'react';
import axios from 'axios';

export const AdminItemList = (props: any) => {
  const navigate = useNavigate();
  const moveAdminItemPo = (itemId: any) => {
      navigate('/admin-item-po', {
        state: {
          data: itemId.id,
        },
      });
    } 
  };

  const moveItemDetail = (itemId: any) => {
    navigate('/itemdetail', {
      state: {
        itemId: itemId.id,
      },
    });
  };

  // 들어온 데이터 넣는것
  const [itemList, setItemList] = useState([]);
  const [sort] = useState(`sort=createDate,desc`);
  //itemName -> 상품명 (search할때 쓰일듯)
  const [searchItem, setSearchItem] = useState('');
  // 페이지 숫자
  const [page, setPage] = useState(0);
  // 전체 페이지 확인(전체 페이지 수만큼 페이지네이션 숫자 늘리기)
  const [totalPages, setTotalPages] = useState(0);
  const [size] = useState(7);
  const [categoryName, setCategoryName] = useState('');
  // select 옵션 선택
  const [filter, setFilter] = useState('itemName');
  // select 박스
  const [searchText, setSearchText] = useState('');

  //검색리스트 길이
  const [listLength, setListLength] = useState(0);

  useEffect(() => {
    const getData = async () => {
        await axios({
          method: 'get',
          //검색창에 검색한 값을 categoryName=${categoryName}&itemName=${searchItem}
         //형식으로 서버에 보내줌
          url: `${process.env.REACT_APP_API_URL}/admin/item/list?
size=${size}&page=${page}&${sort}&categoryName=${categoryName}&itemName=${searchItem}`,
        }).then((res) => {
          setItemList(res.data.content);
          setTotalPages(res.data.totalElements);
          setListLength(res.data.numberOfElements);
        });
      } 
    };
    getData();
  }, [sort, searchItem, size, page, categoryName, navigate]);

//브라우저상에 나오는 데이터 리스트
  const DataList2 = (data: any) => {
    return (
      <S.DataList>
        {listLength !== 0 ? (
          data.map((el: any, index: any) => {
            return (
              <S.Container key={index}>
                <div onClick={() => moveItemDetail(el)}>{el.id}</div>
                <div onClick={() => moveItemDetail(el)}>{el.name}</div>
                <div onClick={() => moveItemDetail(el)}>{el.categoryName}</div>
                <S.AdminButton onClick={() => moveAdminItemPo(el)}>수정</S.AdminButton>
              </S.Container>
            );
          })
        ) : (
          <S.NoSearchItem>검색 결과가 없습니다.</S.NoSearchItem>
        )}
      </S.DataList>
    );
  };

  const TitleList = () => {
    return (
      <S.AdminTitle>
        <div>No</div>
        <div>상품명</div>
        <div>카테고리</div>
      </S.AdminTitle>
    );
  };

//드롭박스
  const OptionList = () => {
    return (
      <S.Select onChange={(e: any) => setFilter(e.target.value)}>
        <option value='itemName'>상품명</option>
        <option value='categoryName'>카테고리명</option>
      </S.Select>
    );
  };

  const search = () => {
    if (filter === 'itemName') {
      // 드롭박스에서 itemName(상품명)을 선택해서 검색창에 검색시
      // 검색값은 setSearchItem에 넣어져서 서버에 request로 보내지게 구현
      setSearchItem(searchText);
      setCategoryName('');
    } else if (filter === 'categoryName') {
      // 드롭박스에서 categoryName(카테고리명)을 선택해서 검색창에 검색시
      // 검색값은 setCategoryName에 넣어져서 서버에 request로 보내지게 구현
      setSearchItem('');
      setCategoryName(searchText);
    }
  };

  const refreshRoute = () => {
      navigate('/admin-item-wr');
  };

  return (
    <S.Wrapper>
      <div>
        <div>
          <S.BigTitle>상품 관리</S.BigTitle>
        </div>
        <Searchcompo2
          filter={filter}
          optionList={OptionList}
          size={size}
          dataList2={DataList2}
          TitleList={TitleList}
          search={search}
          setSearchText={setSearchText}
          data={itemList}
          page={page}
          setPage={setPage}
          totalPages={totalPages}
        />
      </div>
      <S.QnAButton onClick={() => refreshRoute()}>등록</S.QnAButton>
    </S.Wrapper>
  );
};

search컴포넌트

search는 다른 페이지에서도 계속적으로 사용하기 때문에 재사용하기 위해 컴포넌트해서 따로 빼주었다.

import React from 'react';
import * as S from './style';
import { useState } from 'react';
import { AiOutlineSearch } from 'react-icons/ai';
import { Pagination } from '../pagination';


export const Searchcompo2 = (props: any) => {
  // 페이지네이션 컴포넌트에 보내야하는 것들
  //현재페이지
  const [currentPage, setCurrentPage] = useState(props.page + 1);
  //페이지당 보여지는 리스트
  const [itemsPerPage] = useState(props.size);

  //브라우저상 보여지는 한계 숫자
  const [pageNumberLimit] = useState(5);
  const [maxPageNumberLimit, setMaxPageNumberLimit] = useState(5);
  const [minPageNumberLimit, setMinPageNumberLimit] = useState(0);

  //검색창에 검색하는 텍스트
  const onChangeText = (e: any) => {
      props.setSearchText(e.target.value);
  };
  
  //검색창에서 엔터치면 검색되게 구현
  const onKeyDownEnter = (e: any) => {
    if (e.key === 'Enter') {
      props.search();
      e.target.blur();
    }
  };

  //검색창을 눌렀을시 페이지네이션이 첫페이지로 이동하게 구현
  const searchClick = () => {
    setCurrentPage(1);
    props.setPage(0);
  };

  return (
    <S.Wrapper>
      <S.Container>
        <S.SearchArea>
          {props.optionList()}//드롭박스
          <S.SearchBar //검색창
            type='text'
            name='search-form'
            id='search-form'
            placeholder='Search for...'
            onClick={searchClick}
            onChange={(e) => onChangeText(e)}
            onKeyDown={(e) => onKeyDownEnter(e)}
            autoComplete='off'
          ></S.SearchBar>
          <S.SearchButton onClick={() => props.search()}>
              <AiOutlineSearch size='2.8rem' color='#ffffff' />
          </S.SearchButton>
        </S.SearchArea>
      </S.Container>
      <props.TitleList />
      {props.dataList2(props.data)}
      <Pagination
        data={props.data}
        totalPages={props.totalPages}
        page={props.page}
        setPage={props.setPage}
        currentPage={currentPage}
        setCurrentPage={setCurrentPage}
        itemsPerPage={itemsPerPage}
        pageNumberLimit={pageNumberLimit}
        maxPageNumberLimit={maxPageNumberLimit}
        setMaxPageNumberLimit={setMaxPageNumberLimit}
        minPageNumberLimit={minPageNumberLimit}
        setMinPageNumberLimit={setMinPageNumberLimit}
      />
    </S.Wrapper>
  );
};

search 컴포넌트 styled-component

import styled from 'styled-components';

export const Wrapper = styled.div`
  display: flex;
  width: 100%;
  height: 100%;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  @media screen and (max-width: 720px) {
    width: 100%;
    height: 100%;
  }
`;

export const Container = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  @media screen and (max-width: 720px) {
    width: 80%;
    height: 100%;
  }
`;
export const SearchArea = styled.div`
  width: 14.4rem;
  height: 0.4rem;
  display: flex;
  justify-content: flex-end;
  /* margin-top: 10.2rem; */
  /* @media screen and (max-width: 720px) {
    height: 100%;
  } */
`;
export const Select = styled.select`
  margin-right: 0.08rem;
  width: 1.25rem;
  height: 0.4rem;
  border: 0.01rem solid #ddd;
  font-size: 0.3rem;
  padding: 0 0.08rem;
  font-weight: 400;
  background-color: #fefefe;
  color: #686868;
  outline: none;
`;
export const SearchBar = styled.input`
  width: 2.4rem;
  height: 0.4rem;
  padding-left: 0.16rem;
  padding-right: 0.56rem;
  font-size: 0.2rem;
  background-color: #fefefe;
  border: 0.01rem solid #ddd;
  color: ${(props) => props.theme.palette.txtblack};
  outline: none;
  @media screen and (max-width: 720px) {
    width: 70%;
    height: 300%;
    font-size: 0.5rem;
  }
`;

export const SearchButton = styled.button`
  width: 0.4rem;
  height: 0.4rem;
  background-color: #289951;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  @media screen and (max-width: 720px) {
    width: 10%;
    height: 13%;
  }
`;

0개의 댓글