[lodash] 버튼 없이 자동으로 검색하기 & 키워드 표시

이주희·2022년 4월 10일
0

Libraries

목록 보기
14/21
post-thumbnail

연관 내용
[검색 프로세스]
[debouncing]
[버튼 눌러서 검색하기]

검색 하기 버튼을 누르지 않아도 자동으로 검색이 되도록 하려면,
검색창의 onChange 함수가 실행될 때 refetch가 일어나면 된다.

하나하나 입력할 때마다 refetch가 일어나면 무수히 많은 요청이 가서 서버에 부하를 줄 수 있으므로, debouncing을 이용해서 일정 기간 텀을 두고 요청을 보내게 만든다.

자동 검색 구현하기

1. lodash 설치

yarn add lodash
yarn add '@types/lodash' --dev

2. import

import _ from 'lodash'

3. lodash의 debounce 사용

  const getDebounce = _.debounce((data) => {
    // data: event.target.value
    // 0.2초 동안 재작업이 없으면 실행되는 부분
    setKeyword(data);
    refetch({ search: data, page: 1 }); // 바로 실행되지 않고, 0.2초 동안 재입력이 일어나지 않으면 그 때 리페치가 실행된다.
  }, 200); // 0.2초

  const onChangeSearch = (event: ChangeEvent<HTMLInputElement>) => {
    getDebounce(event.target.value);
  };

검색한 부분 스타일 주기

원리
전체 문자열을 단어 단위로 쪼개놓고 span 태그로 만들어준다.
검색된 태그에만 스타일을 준다.

1. keyword 스테이트를 만들고, 검색한 단어를 저장한다.

const [keyword, setKeyword] = useState("");

const getDebounce = _.debounce((data) => {
    setKeyword(data);
  }, 200); 

2. 문자열 split

  • 검색어를 시크릿 코드를 감싼 문자열로 바꾸고(replaceAll),
  • 시크릿 코드를 기준으로 문자열을 나눈다.(split)
{el.title
  .replaceAll(keyword, `#$%${keyword}#$%`)
  .split("#$%")
  .map((el) => (
  <Word key={uuidv4()} isMatched={keyword === el}>
  {el}
</Word>
))}

3. isMatched가 true일 경우에 스타일 지정

interface IProps {
  isMatched: boolean;
}

const Word = styled.span`
  color: ${(props: IProps) => (props.isMatched ? "red" : "black")};
`;

전체 코드

import { useQuery, gql } from "@apollo/client";
import styled from "@emotion/styled";
import { ChangeEvent, useState } from "react";
import {
  IQuery,
  IQueryFetchBoardsArgs,
} from "../../src/commons/types/generated/types";
import _ from "lodash";
import { v4 as uuidv4 } from "uuid";
import * as S from "./day20.style";

const FETCH_BOARDS = gql`
  query fetchBoards($search: String, $page: Int) {
    fetchBoards(search: $search, page: $page) {
      _id
      writer
      title
      contents
    }
  }
`;

export default function MapBoardPage() {
  const [keyword, setKeyword] = useState("");

  const { data, refetch } = useQuery<
    Pick<IQuery, "fetchBoards">,
    IQueryFetchBoardsArgs
  >(FETCH_BOARDS);

  const getDebounce = _.debounce((data) => {
    // data: event.target.value
    // 0.2초 동안 재작업이 없으면 실행되는 부분
    setKeyword(data);
    refetch({ search: data, page: 1 }); // 바로 실행되지 않고, 0.2초 동안 재입력이 일어나지 않으면 그 때 리페치가 실행된다.
  }, 200);

  const onChangeSearch = (event: ChangeEvent<HTMLInputElement>) => {
    getDebounce(event.target.value);
  };

  const onClickPage = (e: any) => {
    refetch({ page: Number(e.target.id) });
  };

  return (
    <>
      <S.Wrapper>
        <S.SearchWrapper>
          검색어 입력
          <S.Search type="text" onChange={onChangeSearch} />
        </S.SearchWrapper>
        {data?.fetchBoards.map(
          (
            el // 인자로 index를 써서 사용할 수 있음: 순서, 필요 없으면 안 써도 됨
          ) => (
            <S.Row key={el._id}>
              <S.Title>
                {el.title
                  .replaceAll(keyword, `#$%${keyword}#$%`)
                  .split("#$%")
                  .map((el) => (
                    <S.Word key={uuidv4()} isMatched={keyword === el}>
                      {el}
                    </S.Word>
                  ))}
              </S.Title>
              <S.Writer>{el.writer}</S.Writer>
            </S.Row>
          )
        )}
        <S.PageWrapper>
          {new Array(10).fill(1).map((_, index) => (
            <S.Page
              onClick={onClickPage}
              id={String(index + 1)}
              key={index + 1}
            >
              {index + 1}
            </S.Page>
          ))}
        </S.PageWrapper>
      </S.Wrapper>
    </>
  );
}
profile
🍓e-juhee.tistory.com 👈🏻 이사중

0개의 댓글