본문 바로가기
Frontend/React

[React] 포켓몬도감 만들기3

by 디스코비스킷 2023. 12. 8.
반응형

15. AutoComplete 기능 생성하기

검색을 했을때 그에 관련된 검색어를 가져올 수 있게 서버에서 포켓몬 이름을 전달해주면 좋지만 그런 API는 없다. 따라서 이 기능을 구현하려면 프론트엔드에서 먼저 모든 포켓몬의 데이터를 가지고 있어야 한다.

현재는 포켓몬 데이터 limit을 20으로 설정하고있기 때문에 소스 코드의 수정이 필요하다.

refactoring

function App() {
  // 모든 포켓몬 데이터를 가지고 있는 State
  const [allPokemons, setAllPokemons] = useState([]);
  // 실제로 리스트로 보여주는 포켓몬 데이터를 가지고 있는 State
  const [displayedPokemons, setDisplayedPokemons] = useState([]);
  // 한번에 보여주는 포켓몬 수 
  const limitNum = 20;

  const url = `https://pokeapi.co/api/v2/pokemon/?limit=1008&offset=0`;


  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  useEffect(() => {
    fetchPokeData();
  }, []);

  const filterDisplayedPokemonData = (allPokemonsData, displayedPokemons = []) => {
    const limit = displayedPokemons.length + limitNum;
    // 모든 포켓몬 데이터에서 limitNum만큼 더 가져오기
    const array = allPokemonsData.filter((_, index) => index + 1 <= limit); // 20n filter
    return array;
  }

  const fetchPokeData = async () => {
    try {
      const response = await axios.get(url);
      setAllPokemons(response.data.results);
      setDisplayedPokemons(filterDisplayedPokemonData(response.data.results));
    } catch (error) {
      console.error(error)
    }
  }

  return (
    ...
      <section className="pt-6 flex flex-col justify-content items-center overflow-auto z-0">
        <div className="flex flex-row flex-wrap gap-[16px] items-center justify-center px-2 max-w-4xl ">
          {displayedPokemons.length > 0 ? (
            displayedPokemons.map(({ url, name }, index) => (
              <PokeCard key={index} url={url} name={name} />
            ))
          ) : (
            <h2 className="font-medium text-lg text-slate-900 mb-1">
              포켓몬이 없습니다.
            </h2>
          )}
        </div>
      </section>
      {allPokemons.length > displayedPokemons.length &&
        displayedPokemons.length !== 1 && (
          <div className="text-center">
            <button
              onClick={() =>
                setDisplayedPokemons(
                  filterDisplayedPokemonData(allPokemons, displayedPokemons)
                )
              }
              className="bg-slate-800 px-6 py-2 my-4 text-base rounded-lg font-bold text-white"
            >
              더 보기
            </button>
          </div>
        )}
    </article>
  );
}

16. AutoComplete 컴포넌트 생성하기

AutoComplete.jsx

검색영역 컴포넌트로 가져오기

const AutoComplete = ({ allPokemons, setDisplayedPokemons }) => {
  const [searchTerm, setSearchTerm] = useState("");
  const debouncedSearchTerm = useDebounce(searchTerm, 500);

  const filterNames = (input) => {
    const value = input.toLowerCase();
    console.log(allPokemons);
    return value ? allPokemons.filter((e) => e?.name.includes(value)) : [];
  };
  const handleSubmit = (e) => {
    e.preventDefault();
    let text = searchTerm.trim();
    setDisplayedPokemons(filterNames(text));
    setSearchTerm("");
  };

  return (
    <div className="relative z-50">
      <form
        onSubmit={handleSubmit}
        className="relative flex justify-center items-center w-[20.5rem] h-6 rounded-lg m-auto"
      >
        <input
          type="text"
          onChange={(e) => setSearchTerm(e.target.value)}
          value={searchTerm}
          className="text-xs w-[20.5rem] h-6 px-2 py-1 bg-[hsl(214,13%,47%)] rounded-lg text-gray-300 text-center"
        />
        <button
          type="submit"
          className="text-xs bg-slate-900 text-slate-300 w-[2.5rem] h-6 px-2 py-1 rounded-r-lg text-center absolute right-0 hover:bg-slate-700"
        >
          검색
        </button>
      </form>
    </div>
  );
};

검색시 autocomplete

  const checkEqualName = (input) => {
    const filteredArray = filterNames(input);
    // 검색어 일치하는게 있으면 없애기
    return filteredArray[0]?.name === input ? [] : filteredArray; // 배열첫번째거에 이름이 있으면 빈배열
  }

UI

{checkEqualName(searchTerm).length > 0 && (
<div
    className={`w-full flex bottom-0 h-0 flex-col absolute justify-center items-center translate-y-2`}
>
    <div className={`w-0 h-0 bottom-0 border-x-transparent border-x-8 border-b-[8px] border-gray-700 -translate-y-1/2`}>
    </div>

    <ul
        className={`w-40 max-h-[134px] py-1 bg-gray-700 rounded-lg absolute top-0 overflow-auto scrollbar-none`}
    >
        {checkEqualName(searchTerm).map((e, i) => (
            <li key={`button-${i}`}>
                <button
                    onClick={() => setSearchTerm(e.name)}
                    className={
                        `text-base w-full hover:bg-gray-600 p-[2px] text-gray-100`
                    }
                >
                    {e.name}
                </button>
            </li>
        ))}
    </ul>

</div>
)}

tailwind scrollbar사용하기

설치: npm install -D tailwind-scrollbar
등록: tailwind.config.cjs에서plugins: [require('tailwind-scrollbar')], 추가

반응형

최근댓글

최근글

© Copyright 2023 jngmnj