[react] 검색 기능 구현

jini.choi·2023년 7월 15일
0

react

목록 보기
1/3

검색 기능 구현

useState, onChange, Array.filter, includes 메소드 사용하여 검색 기능 구현


1.input에 데이터를 저장하기 위해 useState생성 및 value 설정

const [searchMonter, setSearchMonter] = useState("");
.
.
.
<input
  className="search"
  placeholder="Search"\
  value={searchMonter}
/>

2. input에 입력되는 값을 state에 담을 함수를 생성 후 input에 추가

  const onChange = (e) => {
    setSearchMonter(e.target.value);
  };
  .
  .
  .
<input
  className="search"
  placeholder="Search"
  onChange={onChange}
  value={searchMonter}
/>

3. 검색기능
Array.filter : 특정 조건을 만족하는 원소들을 찾아서 그 원소들을 가지고 새로운 배열을 만드는 함수
includes : 문자열이 특정 문자열을 모함하는지 확인하는 함수
toLowerCase : 문자열을 소문자로 변환해서 반환하는 함수

검색 기능 구현 시 toLowerCase를 사용하는 이유는 예외 상황에 따라서 대소문자가 다를 경우 기능 구현이 안되기 때문에 대소문자를 통일해야한다.

  const filterMonster = monstersData.filter((monsterinfo) =>
    monsterinfo.name.toLowerCase().includes(searchMonter.toLowerCase())
  );

처음 마운트될 때는 monstersData 호출하고 검색하여 리랜더링이 될때 filterMonster 호출

   <ul>
        {monstersData &&
          filterMonster.map((monster) => (
            <li className="monster" key={monster.id}>
              <img
                src={`https://robohash.org/${monster.id}?set=set2&size=180x180`}
                alt={monster.name}
              />
              <p>{monster.name}</p>
              <span>{monster.email}</span>
            </li>
          ))}
      </ul>

전체 코드

import React, { useState, useEffect } from "react";
import "./Monsters.scss";

function Monsters() {
  const [monstersData, setMonstersData] = useState([]);
  const [searchMonter, setSearchMonter] = useState("");

  const onChange = (e) => {
    setSearchMonter(e.target.value);
  };

  const filterMonster = monstersData.filter((monsterinfo) =>
    monsterinfo.name.toLowerCase().includes(searchMonter.toLowerCase())
  );

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then((res) => res.json())
      .then((data) => setMonstersData(data));
  }, []);

  return (
    <div className="monsters">
      <h1 className="title">Monsters</h1>
      <input
        className="search"
        placeholder="Search"
        onChange={onChange}
        value={searchMonter}
      />
      <ul>
        {monstersData &&
          filterMonster.map((monster) => (
            <li className="monster" key={monster.id}>
              <img
                src={`https://robohash.org/${monster.id}?set=set2&size=180x180`}
                alt={monster.name}
              />
              <p>{monster.name}</p>
              <span>{monster.email}</span>
            </li>
          ))}
      </ul>
    </div>
  );
}

export default Monsters;

😱 반성

처음 monstersData를 아래와 같이 선언하였다.

const [monstersData, setMonstersData] = useState();

그 결과 앱 실행 시 처음에는 정상 호출이 되지만 새로고침을 하게되면 에러 뚜둥..(당연하지..)
Cannot read properties of undefined (reading 'filter')

에러가 나는 원인은

useEffect의 두 번째 매개변수로 빈 배열([])을 전달하였기 때문에, useEffect는 컴포넌트가 처음 마운트될 때 한 번만 실행되고, 업데이트될 때는 실행되지 않는다.

문제는 컴포넌트가 마운트될 때 API 요청이 이루어지기 때문에, 해당 요청이 완료되기 전에 monstersData가 비어있게 된다.

하여, 초기 상태에서 filterMonsterundefined인 상태가 될 수 있으며, 이후로는 monstersData가 변경될 때마다 API 요청이 다시 일어나지 않으므로 monstersData가 업데이트되지 않는 한 계속해서 이전의 undefined 값을 가지게 된다.

해결 방법으로는 filterMonster를 렌더링 전에 체크하여 undefined인 경우 기본값을 설정해야하는데,
monstersData를 빈 배열([])로 초기화하고, API 요청이 완료되기 전에 몬스터 목록이 빈 배열로 렌더링되도록 해준다.

const [monstersData, setMonstersData] = useState([]);
profile
개발짜🏃‍♀️

0개의 댓글