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
가 비어있게 된다.
하여, 초기 상태에서 filterMonster
가 undefined
인 상태가 될 수 있으며, 이후로는 monstersData
가 변경될 때마다 API 요청이 다시 일어나지 않으므로 monstersData
가 업데이트되지 않는 한 계속해서 이전의 undefined
값을 가지게 된다.
해결 방법으로는 filterMonster
를 렌더링 전에 체크하여 undefined인 경우 기본값을 설정해야하는데,
monstersData
를 빈 배열([])
로 초기화하고, API 요청이 완료되기 전에 몬스터 목록이 빈 배열로 렌더링되도록 해준다.
const [monstersData, setMonstersData] = useState([]);