불필요한 http request를 잡아랏! (feat. Component Life Cycle)
✔ 현재 상황
Blog 컴포넌트가 mount 되면
componentDidMount에서 http request를 보낸다.
-> 받아온 data를 state에 넣어준다.
-> state에서 data를 가져와 map 메소드를 이용해 Post 컴포넌트들을 만들어준다.
Blog.js
import React, { Component, Fragment } from "react";
import Post from "../Post/Post";
import FullPost from "../../containers/FullPost/FullPost";
import axios from "axios";
import "./Blog.css";
class Blog extends Component {
state = {
error: false,
posts: [],
selectedPostId: null,
};
componentDidMount() {
console.log("[Blog.js] componentDidMount");
axios
.get("http://jsonplaceholder.typicode.com/posts")
.then((response) => {
const data = response.data.slice(0, 4);
const updatedData = data.map((post) => {
return { ...post, author: "Ellen" };
});
this.setState({ posts: [...updatedData] });
})
.catch((error) => this.setState({ error: true }));
}
componentDidUpdate() {
console.log("[Blog.js] componentDidUpdate");
}
showFullPostHandler = (id) => {
this.setState({ selectedPostId: id });
};
render() {
console.log("[Blog.js] render");
let posts = <h1>Something went wrong...</h1>;
if (!this.state.error) {
posts = this.state.posts.map((post) => (
<Post
key={post.id}
data={post}
clicked={() => this.showFullPostHandler(post.id)}
/>
));
}
return (
<Fragment>
<section className="Posts">{posts}</section>
<section>
<FullPost id={this.state.selectedPostId} />
</section>
</Fragment>
);
}
}
export default Blog;
이렇게 Post 컴포넌트 4개가 렌더되었다.
✔ 문제의 시작
이 4개의 Post 컴포넌트들 중 하나를 클릭하면
그 Post의 전체 내용이 담긴 FullPost 컴포넌트를 렌더하고 싶었다.
이때, 클릭한 컴포넌트의 아이디를 FullPost에 props로 넣어주었다.
작성한 FullPost.js 코드
import React, { Component } from "react";
import axios from "axios";
import "./FullPost.css";
class FullPost extends Component {
state = {
loading: true,
error: false,
post: null,
};
componentDidUpdate() {
console.log("[FullPost.js] componentDidUpdate");
const id = this.props.id;
axios
.get(`http://jsonplaceholder.typicode.com/posts/${id}`)
.then((response) => {
const data = response.data;
console.log(data);
})
.catch((error) => {
console.log("oops");
this.setState({ error: true });
});
}
componentDidMount() {
console.log("[FullPost.js] componentDidMount");
}
render() {
console.log("[FullPost.js] render");
if (!this.props.id) {
return null;
}
return <article>hey</article>;
}
}
export default FullPost;
Post를 클릭하기도 전에!!!
그냥 첫 화면만 띄웠는데도 오류가 뜨고 난리 났다....................ㅠㅠㅠㅠㅠ
error 메시지 내용을 보니
http request를 보낼때 url 주소 마지막에 null이 들어가서 찾을 수 없다는 것..
아늬
아늬
아늬@@
근데
나는
Post를 클릭하면
FullPost의 componentDidUpdate에서 http request를 보내는 걸로 설계했는데
웨웨웨
Post를 클릭하기도 전에!!!!!!!
http request를 보냈는지 나는 도저히 이해를 못했음....
한 5분동안 멘붕이 왔고 도대체 어디서부터 잘못된 건 지 모르겠어서
각 컴포넌트 render, componentDidMount, componentDidUpdate로 테스트를 해봤다.
어떻게?
예를 들어,
componentDidUpdate(){
console.log('[FullPost.js] componentDidUpdate');
}
이렇게 해서 각 Lifecycle이 언제 돌아가는지 다 체크해본 것 ... ㅎㅎㅎ
빨간 줄을 기준으로 위의 4개는 처음 페이지가 렌더되었을 때다.
(이미 이때 FullPost가 render되고 componentDidMount까지 한 바퀴 돌았다.)
그리고 Blog.js가 componentDidMount되면!!!!(여기서부터 내 문제의 시작)
(기본으로 UI에 깔리는 포스트 4개를 받아오기 위해서)
componentDidMount에서 http request를 보낸다.
데이터를 잘 받아오면 Blog.js가 업데이트 되는데...(두둥)
리액트 컴포넌트들은 부모 컴포넌트가 업데이트 되면 자식 컴포넌트도 같이 업데이트 된다는 것!!!!!!!!!!!!!!!!
그래서 Post를 그냥 받아오는 과정이었고
Post를 클릭하지도 않았는데
Blog가 업데이트 되는 바람에
FullPost도 업데이트 되었고!!!!!!!!!!
FullPost의 componentDidUpdate에 넣어놨던
http request가 보내지면서 오류가 발생한 것...
✔ 해결
그러면 Post를 받아오느라고 Blog가 업데이트 될때,
FullPost는 업데이트 안 되게 하면 되잖아!!!!!!!!!!
라는 생각이 떠올랐고
불필요한 업데이트를 막아주는 shouldComponentUpdate가 생각났다@@
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.id !== this.props.id) {
return true;
}
return false;
}
FullPost에 이렇게 shouldComponentUpdate 라이프 사이클을 추가해서
props에 들어오는 id값에 변화가 없으면
(즉, 새로운 Post를 클릭한 게 없으면)
FullPost는 업데이트가 안되도록!
그래서 componentDidUpdate에서 불필요한 http request를 보내지 않도록!!!!!!!!했고
there you go!!!!!
문제 해결 ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ
이제 FullPost에서 불필요한 http request를 보내서 오류가 생기는 문제를 해결했다.
빨간선 기준으로
첫번째 블록의 로그들은
제일 처음 컴포넌트들이 렌더 되었을 때.
그리고 두번째 블록은
Blog 컴포넌트에서 http request를 보내서 데이터를 받아온 후 업데이트가 일어났을 때!
(이떄는 Post 컴포넌트만 새로 render되지 FullPost는 건들이지 않는다.)
세번째 블록은
Post 컴포넌트 중 하나를 클릭했을 때,
이때에 드디어 FullPost 컴포넌트가 업데이트 된다!!!!
Problem solved 😎😎😎