React에서 fetch 이해하고, map과 filter 사용하여 검색기능 적용하기 💯
fetch는 필요한 데이터를 받아서 component형태로 사용할 때 주로 사용하는 방식
이며 절차는 아래와 같다.
구조는 아래와 같은 형식이다.
아래의 경우는 get method로 불러올 때의 경우이고 , API를 불러오는 method가 post인 경우 method정보를 인자로 받아야 하기 때문에 API 주소형태가 다를 수 있다.
fetch("http~~~~~1")//API 주소가 string 형태로 들어감
.then(response => response.json())// JSON BODY를 JS로 변환해줌
.then((response) => this.setState ({users : response})
//받은 데이터를 user로 setState에 저장해줌, 보통이런형태로 사용되며,
//데이터가 잘 들어온지 확인하려면 console.log로 확인해보면 된다.
컴포넌트가 호출되는 상태 (DOM 으로 들어간 상태)
를 의미하며, UnMount는 컴포넌트가 더 이상 필요 없는 상태
를 의미한다. re-render
되게된다. 기본적으로 우리가 다루고 아는 부분은 render()가 re-render 된다는 부분 이지만 다른 method가 있다면 아래 해당하는 method도 다시 업데이트가 되는 것 이다.: DOM에서 해당 Component를 제거 할 때 사용 된다.
- componentWillUnmount()
: 보통 timers 를 무효화 시키거나, newwork request를 없앨 때, 인데, setState()를 통해 상태를 업데이트하는것이 아니다. 왜냐하면 re-render 시키려는게 아니라 아예 없애려고 하는 기능이기 때문
이다.
Fetch
는 한번만 불러와야 하기 때문에 render 안에 위치하면 loop이 돌 수 있다. 그렇기 때문에 한번 render 되는 ComponentDidMount()
에 위치시켜야 한다.
사용 예시
ComponentDidMount() {
fetch();
.then((response) => response.json() )
.then((response) => this.setState ({users : response})
CardList
, Card
, SearchBox
components 들이 필요 하고, 전체를 나타내줄, index.js
가 있어야 하겠다. (각각의 css 파일 또한 기본) 이전에 작성해보았던 Lifecycle을 고려해 보았을 때, API호출은 componentDidMount()
안에 위치하게 해야한다. (API호출은 딱 한번만 render 해야 되기 때문)
호출 할 api주소를 ' '
안에 작성하고, .then
함수를 통해 받은 JSON을 JS로 변환
해주고, .then
함수를 다시 호출해서 받은 데이터를 이전에 this.state
에 빈 객체로 설정해준 monsters 객체
에 저장해준다.
state = {
mosnters : []
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(res => this.setState ({monsters : res}))
}
monsters라는 이름의 props
를 주고, 그 안에 state에 저장해준 monsters 데이터를 전달
해준다. <CardList monsters={this.state.monsters}/>
현재는 정보를 통해 이름,이메일,이미지,를 포함한 10가지의 정보만 들어왔지만 우리가 필요한 정보만 보여주게끔 보여주고 싶은 키 값만 가져오면 된다
CardList component에 Card component를 전달해준다. Card.js에 담긴 내용은 아래인데, this.props.id
this.props.name
, this.props.email
을 통해 props 값을 가져온다고 정의해 두었으니, Card component에서 id,name,email이름을 주고 필요한 정보를 불러 와야 한다.
Card.js 코드
class Card extends Component {
render() {
return (
<div className="card-container">
<img
src={`https://robohash.org/${this.props.id}?set=set2&size=180x180`}
alt=""
/>
<h2>{this.props.name}</h2>
<p>{this.props.email}</p>
</div>
);
}
}
export default Card;
Card component를 import하고, CardList.js 파일에 전달하고자 하는 키 값을 부모인 index.js로 부터 가져온 값 (this.props.monsters) 에 map 함수로 접근한다.
map 함수를 사용해서 monsters 데이터 객체 하나하나에 접근하여 보여주고 싶은 데이터를 출력하면 된다. (monsters는 데이터를 포함한 여러객체를 가지고 있는 array 이다. monsters = [ { },{ },{ } ]
이렇게 생겼기 때문에 인자 값 을 주고 가져오고자 하는 값을 dot notation
을 통해 가져오면 된다.
각 { } 을 돌면서 <Card component>
에 원하는 값을 포함시켜서 출력하게 끔 하는 상태라고 보면된다. 나는 item을 인자 값으로 주어서 각 객체의 .id
, .name
.email
을 가져오는 식으로 표현 한 것이다.
import React, { Component } from "react";
import "./CardList.css";
import Card from "../Card/Card"; // Card를 import 한다
class CardList extends Component {
render() {
return (
<div className="card-list">
{this.props.monsters.map((item,idx) => { return (
<Card key={idx} id ={item.id} name={item.name} email={item.email} />
)})}
</div>
)
}
}
export default CardList;
map 함수
로 컴포넌트 재사용 시 두가지 인자를 넣어줄 때 두번째 값은 index를 의미
한다.
React 에서 컴포넌트에서 재사용 시 unique한 key 값을 설정하지 않으면
, 코드는 렌더링 되지만 console에서 key 값이 설정되지 않았다는 오류(*Each child in an array should have a unique "key" prop*) 가 뜬다.
항상 unique한 키 값을 설정해 주어야 한다. 👀
현재 카드에서 unique한 요소를 찾지못해 index로 저장했는데 아래 블로그를 보니 index값으로 저장하는 것인 좋은 practice는 아니라고 한다. 참고 블로그
React에서 key는 DOM element를 idenify할 수 있는 유일한 요소이기 때문에 중간에 추가 되거나, 삭제되는 데이터가 있을 때, 키 값이 그대로이면 오류를 발생할 가능성이 높다. a key is the only thing React uses to identify DOM elements. What happens if you push an item to the list or remove something in the middle? If the key is same as before React assumes that the DOM element represents the same component as before. But that is no longer true.
최대한 가진 데이터 에서 key 값을 지정해 사용하는 걸로 인식하고 있어야겠다.
input
을 별도의 SearchBox 컴포넌트 안에
만들고 index.js에 원하는 위치에 component로 전달 해준다. onChange event
를 적용해서 입력 값을 setState의 UserInput
에 저장할 수 있게 끔 한다. import React, { Component } from "react";
import "./SearchBox.css";
class SearchBox extends Component {
render() {
return (
<input
className="search"
type="search"
placeholder="Search..."
onChange={this.props.handleChange}
/>
);
}
}
export default SearchBox;
SearchBox에 handleChange 를 주고
그 값으로 this.handleChange
를 입력해, 원하는 함수(this.handleChange) 를 render()
위에 구현 해준다. state = {
monsters: [],
userInput: ""
};
-------------------------------
handleChange = (e) => {
this.setState({userInput : e.target.value})
}
-------------------------------
<SearchBox handleChange={this.handleChange}/>
순서대로 보자면...
monsters 객체의 각 요소에 접근해 각 요소의 name 값이, userInput 값에 포함 되는 요소라면 filter해서 return 하게 하고 그 return 된 값을 monstersFilter에 저장 한다.
그냥 입력할때는 보통 소문자로 입력하기 때문에 각 요소의 name값에 toLowerCase()
해주는 것이다.
const monstersFilter = this.state.monsters.filter((obj) =>
obj.name.toLowerCase().includes(this.state.userInput.toLowerCase()));
this.state.monsters
로 정해줬던 Cardlist 의 props 값을 filter 메소드가 반환하는 변수로
저장한다. <CardList monsters={monstersFilter}/>
React method (map,filter)를 사용한 과제를 구현해 보았는데 부족한 점은 더 블로깅 하고 넘어가서 새로운 프로젝트를 준비할 수있도록 해야겠다.
정리할 땐 아하! 하다가도 실제 다른 코드로 구현 하면 아직은 부족하므로..ㅜㅜ 다시 더 보고 볼것! 💁