filter
메서드는 배열의 각 요소에 대해서 주어진 함수의 결과값을 만족하는(true인) 요소를 모아서 새로운 배열로 반환해주는 함수입니다.
배열에 요소가 없다면 함수를 실행하지 않고, 원본 배열을 변경하지 않습니다.
const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present'];
const result = words.filter(word => word.length > 6);
console.log(result);
// expected output: Array ["exuberant", "destruction", "present"]
이 메서드를 이용하여 어떻게 검색 기능을 구현할 수 있을까요?
filter
메서드의 기능을 생각해본다면, 배열의 모든 요소들 중에 검색창에 입력한 값(input의 value)과 일치하는 값을 포함하고 있는 요소만을 반환하도록 하면 될 것 같은데요?
그렇다면, 배열의 요소가 검색창의 입력값과 일치하는 값을 포함하고 있는지는 어떻게 알 수 있을까요?
const array1 = [1, 2, 3];
console.log(array1.includes(2));
// expected output: true
const pets = ['cat', 'dog', 'bat'];
console.log(pets.includes('cat'));
// expected output: true
console.log(pets.includes('at'));
// expected output: false
includes
메서드는 배열이 특정 요소를 포함하고 있는지 아닌지를 판별해 boolean값, 즉 true or false로 반환해 줍니다.
앞서 filter
메서드가 주어진 함수의 조건에 결과값이 true인 요소들만 반환해준다고 했던 것, 기억하시나요?
이제 두 메서드를 이용해 리액트에서 검색 기능을 구현해 봅시다.
위에 보시는 monster 카드들은, 각각의 이름과 이메일 정보를 각각 객체로 담고 있는 monsters라는 배열(array)에 속해 있습니다.
맨 위의 검색창(searchbox)이 보이시나요? 검색창 컴포넌트는 input태그로 구현되어 있구요.
우리는 아래와 같이 검색창에 특정 값을 입력하면, 이름에 그 값을 포함하고 있는 monster 카드들만 보이게 하고 싶습니다.
우선 검색창(SearchBox) 컴포넌트의 구조를 보면 다음과 같습니다.
import React from "react";
import "./SearchBox.scss";
function SearchBox(props) {
return (
<input
className="search"
type="search"
placeholder="Search..."
onChange={props.handleChange}
/>
);
}
export default SearchBox;
보시다시피, 검색창은 부모 요소로부터 props로 이벤트 함수를 받아오고 있습니다. 검색창에 입력 이벤트가 일어나면, handleChange 함수가 실행되겠군요.
그럼 검색창 컴포넌트를 품고 있는 부모 요소를 확인해볼까요?
import React, { useState, useEffect } from "react";
import SearchBox from "./Components/SearchBox/SearchBox";
import CardList from "./Components/CardList/CardList";
import "./Monsters.scss";
function Monsters() {
const [monsters, setMonsters] = useState([]);
const [userInput, setUserInput] = useState("");
const handleChange = (e) => {
setUserInput(e.target.value);
};
const searchedMonster = monsters.filter((monster) => {
return monster.name.toUpperCase().includes(userInput.toUpperCase());
});
return (
<div className="monsters">
<SearchBox handleChange={handleChange} />
<CardList key={monsters.i} monsters={searchedMonster} />
</div>
);
}
export default Monsters;
(monsters 배열은 fetch 함수로 외부에서 받아오고 있습니다)
검색창 컴포넌트가 props로 받고 있는 handleChange 함수를 찾으셨나요?
사용자가 값을 입력하면, setState 함수를 통해 userInput에 저장되고 있네요.
바로 이 userInput값이 monster의 이름에 포함이 되었는지 아닌지를 확인하고, 포함된 카드들(true인)만 화면에 보여주게끔 함수를 만들면 되지 않을까요?
위 코드의 searchedMonster
가 바로 그렇게 만들어진 함수입니다.
monsters 배열에 filter
메서드가 사용되었네요. monsters 배열의 각 요소인 monster를 모두 확인한 후, return문의 조건에 해당하는 monster 카드만 새로운 배열로 반환하도록 해줍니다. return문을 자세히 봅시다.
return monster.name.toUpperCase().includes(userInput.toUpperCase());
(monsters배열의 요소인 monster는 name이란 키를 가진 객체입니다)
앞서 말했던 includes
메서드가 사용되었네요. 이 메서드가 인자로 받은 userInput이, monster.name에 포함되었는지 아닌지를 확인하고 있습니다.
그런데 toUpperCase
는 무엇이며, 왜 사용된걸까요?
toUpperCase
는 영문 문자열을 모두 대문자로 바꿔줍니다. 모두 소문자로 바꿔주는 toLowerCase
라는 메서드도 있죠. 이 메서드를 사용한 이유는, 사용자가 대문자나 소문자, 어느 것으로 검색해도 같은 결과를 얻기 위해서입니다.
위 코드처럼 toUpperCase
를 통해 사용자의 입력값과 monster의 이름을 모두 대문자로 한번 바꾸어준다면, 대소문자 여부에 상관없이 일치하는 값을 찾을 수 있겠죠? 같은 맥락으로 toLowerCase
를 사용해도 됩니다.
마지막으로 searchedMonster
함수를 props로 monster 카드 자식 컴포넌트(CardList)에 넘겨주기만하면 끝입니다. 이제 CardList 컴포넌트는 searchedMonster
를 통해 필터링된 monster 카드만을 렌더링 해 줄 것입니다.
대문자 S를 입력해도, 이름에 소문자 s를 가진 monster 카드도 잘 보여지고 있습니다.