React Class형 컴포넌트로 만들어야 한다는 미션을 받았다.
헉 React를 class로요..?
그래, 사실은 React 도 class형 컴포넌트로 가득찬시기가 있었다. 하지만 React v16.8부터 React Hook 을 지원해주면서 공식문서에서도 함수형 컴포넌트와 함께 사용할 것을 권장했기 때문에 처음 React를 배울 때 이후로 함수형으로 구현 하던 나였는데...
대외적으로는 "함수형이 권장이니까~"라고 말하지만 사실은 Class로 컴포넌트 구현하는 법을 잊어버린 나 자신을 감추며 간단히 React 기반의 상품 필터링을 구현했다.
Class 컴포넌트를 사용하며 느꼈던점 Hook의 부재로 인한 상태관리 삽질을 정리해보았다.
import React, { Component } from 'react'
export default class RecentList extends Component {
render() {
return (
<div>
</div>
)
}
}
import React from 'react'
export default function RecentList() {
return (
<div>
</div>
)
}
로직과 상태를 컴포넌트 내에서 구현 = stateful
props
를 받아 this.props
로 액세스 함componentDidMount
,componentDidUpdate
...)로직과 상태를 props
로 받아 뿌려줌 = stateless
useState
)useState
: 상태 저장useEffect(callback,[])
: 수명주기 동작 조정useEffect
에서 callback을 트리거하여 componenetDidMount
수명주기동작을 복제한다.comoponentWillUnMount
기능 수행
gif용량변화로 인해 영상이 깨지는점..양해바랍니다....
export default class RecentList extends Component {
state = {
recentFiltered: [],
brandList: [],
isUnlike: '',
};
//중략
state | 설명 |
---|---|
recentProducts | localStorage에서 가져온 최근 상품항목 |
recentFiltered | 필터링 될 때마다의 상품리스트 |
brandList | 최근 상품 항목들의 브랜드 종류 리스트 |
각각의 필터를 클릭했을 시 클릭이벤트가 this.setState
해 recentFiltered를 업데이트 하는 방식
setLowPriceOrder = () => {
const { recentFiltered } = this.state;
const lowOrderedList = recentFiltered.sort((a, b) => {});
this.setState({ recentFiltered: lowOrderedList });
};
setHighPriceOrder = () => {
const { recentFiltered } = this.state;
const highOrderedList = recentFiltered.sort((a, b) => {});
this.setState({ recentFiltered: highOrderedList });
};
setRecentViewOrder = () => {
this.setState({ recentFiltered: getStore('recentViewed')});
};
데이터 정규화가 안 된 상태
state는 서로의 state를 가지고 있으면 안되고 함수형 체이닝을 통해 재연산을 해야지 중복적으로 가지고 있으면 상태변경에 대한 추적이 어렵다.
함수형 체이닝이 아닌 함수 자체에서 각각 setState
를 통해 상태를 변경하기 때문에 유지,보수와 상태변경 추적이 매.우 어렵다
실제로) 잦은 state변경으로 인해 변경하지도 않은 원본상품리스트가 변경되는 sideEffect 발생
중복된 state인 recentProducts를 제거하여 각 함수에서는 props로 해당 값을 받아서 사용하게 변경해야 한다.
현재 함수는 직접 state를 변경하지만 함수는 props(localStorage에서 가져온 최신상품리스트)를 이용해 필터링한 상품리스트만 return한다.
<개선 전>
render() {
const { recentFiltered } = this.state;
return (
{recentFiltered.map((product) => {
const { id } = product;
<개선 후>
render() {
const recentProducts = localSotrage.getItem(~
const viewdProducts = setHighPriceOrder(recentProducts)
return (
{viewdProducts.map((product) => {
const { id } = product;
즉 기존에는 다 필터링된 상품리스트를 this.state에서 가져와 렌더링했지만
localStorage에서 가져온 데이터를 메서드로 필터링한 후 바로 렌더링 하는 것이다.
즉 필터링한 리스트를 상태로 가지고 있을 필요없이 바로 렌더링 해주면 되는 방향으로 변경하는 것이다.
이로 인해 고통받으며 설계 시 고려해야 할 점을 정리해보았는데,
특히 this.state
는 전역변수와 같은 존재인데, 모든 함수에서 이 state의 상태를 갱신하고 조작했다.
상태는 사용자의 입력에 따라 반응해야 하는데, Component 렌더링에 매우 의존적이게 되었고 이로 인해 추적 또한 어려워졌다.
2가지 상태(원본상품리스트, 필터링상품리스트)를 다 가지고 있었던 이유는 localSotrage에 계속적으로 접근하면 안좋다고 막연하게 생각해서였는데,
일단 localStorage는 성능상에 그렇게 큰 영향을 끼치지 않고 만약 성능상의 문제라면 component가 재렌더링에 더 초점을 둬야 한다는 결론은 얻었다.
localStorage에서 가져올 데이터는 사실상 실무에서는 서버에서 API 호출이다가져오는 것인데 실시간으로 최신상품을 보여줘야 하기 때문에 성능저하를 감수하더라도 매번 데이터를 가져오는 것이 필요하다는 것이 결론이다.
또 localStorage(=미래API요청)은 한곳에서 호출되면 네트워크 이슈 발생 시 추적이 편리하기 때문에 render에서 한번만 불러오는 것이 디버깅하기 쉽다.
상태관리 다음엔 제대로 해줄게.
현재 위코드와 프리온보딩 코스에 참여하고 있는데,
평소에 지나쳤던 기본적인 부분들을 다시금 체크하고,
또 그냥 뚝딱 만드는것에만 집중하지 않고 왜왜왜 했는지 조원분들이랑 공유하면서 많이 공부하고 있다.
모두 너무 감사합니다~~