- 리덕스 reselect 사용하기 목차
reselect를 알기 전, Selector를 먼저 알아야합니다.
| Selector란 ?
- state 에서 필요한 데이터를 가져오거나, 계산을 수행해서 원하는 형태의 데이터를 가져오는 역할을 말합니다.
왜 사용할까?
selector는 Redux가 적은 양의 필요한 데이터만을 갖고 있게 data들의 연산을 도우며, state를 가져오는 컴포넌트들과 state의 구조 사이에 1개의 층(selector)를 두어 구조가 바뀌어도 연관된 모든 컴포넌트를 바꿀 필요 없이 selector
만 바꿔주면 성능이 향상되기 때문에 사용합니다.
| reselect 란?
selector 역할을 수행하면서 캐싱을 통해 동일한 계산을 방지해 성능을 향상시켜줍니다.
연산을 효율적으로 처리할 수 있게 도와주는 역할을 합니다.
즉, 새롭게 계산하지 않고 저장된 결과 값을 돌려주는 라이브러리 인 메모이제이션
기능 지원합니다. (성능적인 장점 )
또한 data를 가공해서 사용하는 코드가 많을 경우에는 선택자함수(useSelector)
를 따로 분리해주는 장점이 있어서 가독성이 높아진다.
(다른 곳에서도 재사용 가능)
createSelector
함수를 통하여 선택자 함수를 만들면 메모이제이션
기능이 동작합니다.
리덕스에는 원본 데이터만 저장하고
필터 연산은 컴포넌트 쪽에서 계산하는 방법입니다.
왜 사용할까 ?
일번적으로는 selector 를 한 파일에 보관하고 관리합니다.
이럴경우에, 한 파일 내 있는 selector가 갱신되면 다른 selector도 갱신된다고합니다.
=> 필요하지 않은 만큼 컴포넌트 렌더링이 발생하겠죠?
이 때, 불필요한 렌더링을 막아주고 성능을 향상시키는 것이 바로 reselector
라는 라이브러리의 memoization
의 기능입니다.
( memoization ?
함수에 대한 입력을 추적하고, 나중에 참조할 수 있도록 입력과 결과를 저장하는 작업을 뜻합니다. 즉, 이전과 동일한 입력으로 함수를 호출하면 함수는 실제 작업을 건너뛰고 해당 입력 값을 마지막으로 수신했을 때 생성한 것과 동일한 결과를 return해줍니다. )
다음은 사용자가 option을 선택한 값에 따라 결과값이 바뀌는 코드입니다.
NumberSelect.js
function NumberSelect({ value, options, postfix, onChange }) {
return (
<div>
<select onChange={e => {
const value = Number(e.currentTarget.value);
onChange(value);
}}
value={value}
>
{options.map(option => (
<option key={option} value={option}>
</option>
))}
</select>
{postfix}
</div>
)
}
FriendMain.js
import { shallowEqual, useDispatch, useSelector } from "react-redux";
import { getNextFriend } from "../../common/mockData";
import FriendList from "../component/FriendList";
import { addFriend, setAgeLimit, setShowLimit } from "../state";
import NumberSelect from '../component/NumberSelect';
import { MAX_AGE_LIMIT, MAX_SHOW_LIMIT } from "../common";
//FriendMain
function FriendMain() {
const [ageLimit, showLimit, friendsWithAgeLimit, friendsWithAgeShowLimit] = useSelector(state => {
//초기 ageLimit : 30
const { ageLimit, showLimit, friend } = state.friend;
//만약 ageLimit:30 보다 같거나 작으면 true 값 반환 즉, 30보다 밑인 애들만 반환
const friendsWithAgeLimit = friend.filter(item => item.age <= ageLimit);
return [ageLimit, showLimit, friendsWithAgeLimit, friendsWithAgeLimit.slice(0, showLimit)];
}, shallowEqual);
const dispatch = useDispatch();
function onAdd() {
const friends = getNextFriend();
dispatch(addFriend(friends));
}
console.log("FriendMain render");
return (
<div>
<button onClick={onAdd}>친구 추가</button>
<hr />
<NumberSelect
onChange={v => {
dispatch(setAgeLimit(v))
}}
value={ageLimit}
options={AGE_LIMIT_OPTIONS}
postfix="세 이하만 보기"
/>
<FriendList friends={friendsWithAgeLimit} />
<NumberSelect
onChange={v => {
dispatch(setShowLimit(v));
}}
value={showLimit}
options={SHOW_LIMIT_OPTIONS}
postfix="명 이하만 보기(연령 제한 적용)"
/>
<FriendList friends={friendsWithAgeShowLimit} />
</div>
)
};
const AGE_LIMIT_OPTIONS = [15, 20, 25, MAX_AGE_LIMIT];
const SHOW_LIMIT_OPTIONS = [2, 4, 6, MAX_SHOW_LIMIT];
export default FriendMain;
state.js
import createReducer from "../common/createReducer";
import { MAX_AGE_LIMIT, MAX_SHOW_LIMIT } from './common';
//액션 생성 변수
const ADD = "friend/ADD";
const REMOVE = "friend/REMOVE";
const EDIT = "friend/EDIT";
//추가 액션
const SET_AGE_LIMIT = "friend/SET_AGE_LIMIT";
const SET_SHOW_LIMIT = "friend/SET_SHOW_LIMIT";
//액션 크리에이터 생성 함수
export const addFriend = friend => ({ type: ADD, friend });
export const removeFriend = friend => ({ type: REMOVE, friend });
export const editFriend = (friend) => ({ type: EDIT, friend });
export const setAgeLimit = ageLimit => ({ type: SET_AGE_LIMIT, ageLimit });
export const setShowLimit = showLimit => ({ type: SET_SHOW_LIMIT, showLimit });
// 초기 값 객체 friend 배열 초기화
const init = {
friend: []
, ageLimit: MAX_AGE_LIMIT
, showLimit: MAX_SHOW_LIMIT
};
//리듀서 생성
//첫 번쨰 값 : 초기 값,
//두 번째 값 : handlerMap
//handlerMap에 [ADD] key값은 state와 action을 인자로 받은 함수 발생
//그럼 새로운 배열객체 생성
const reducer = createReducer(init, {
//객체를 만들어서 handler 함수를 작성
[ADD]: (state, action) => state.friend.push(action.friend),
[REMOVE]: (state, action) => state.friend = state.friend.filter(
friend => state.friend.id !== action.friend.id
)
, [EDIT]: (state, action) => {
const index = state.friend.findIndex(
friend => friend.id === action.friend.id
);
if (index >= 0) {
state.friend[index] = action.friend;
}
},
[SET_AGE_LIMIT]: (state, action) => {
state.ageLimit = action.ageLimit
}
, [SET_SHOW_LIMIT]: (state, action) => {
state.showLimit = action.showLimit
}
});
export default reducer;
위의 코드는 reselector 를 이용하지 않은 코드입니다.
아래에서는 reselector
를 이용한 코드를 활용해보겠습니다.
선택자함수 selector.js 추가
import { createSelector } from "reselect";
//3가지 data를 단순히 가져오는 함수
const getFriends = state => state.friend.friend;
export const getAgeLimit = state => state.friend.ageLimit;
export const getShowLimit = state => state.friend.showLimit;
//reselect 함수 사용
import { createSelector } from "reselect";
//3가지 data를 단순히 가져오는 함수
const getFriends = state => state.friend.friend;
export const getAgeLimit = state => state.friend.ageLimit;
export const getShowLimit = state => state.friend.showLimit;
//reselect 함수 사용
export const getFriendsWithAgeLimit = createSelector(
[getFriends, getAgeLimit],
//선택자 함수
//위에서 반환하는 값(getFriends,getAgeLimit) 을 받아서 작성
//해당 함수는 friends 와 ageLimit이 변경되지 않았더라면 filter를 사용하지 않고 이전의 연산 값을 재사용.
(friends, ageLimit) => friends.filter(item => item.age <= ageLimit),
);
//reselect 함수 사용
export const getFriendsWithAgeShowLimit = createSelector(
[getFriendsWithAgeLimit, getShowLimit],
//마찬가지로 해당 함수는 friendsWithAgeLimit 와 showLimit 가 변경되지 않았더라면, 이전의 값을 사용함.
(friendsWithAgeLimit, showLimit) => getFriendsWithAgeLimit.slice(0, showLimit)
);
선택자 함수를 이용한 FriendMain.js
function FriendMain() {
const [ageLimit, showLimit, friendsWithAgeLimit, friendsWithAgeShowLimit] = useSelector(state =>
[
//선택자 함수를 이용하여 사용
getAgeLimit(state)
, getShowLimit(state)
, getFriendsWithAgeLimit(state)
, getFriendsWithAgeShowLimit(state)
]
, shallowEqual);
위와 같이 선택자 함수를 이용하여 변경할 수 있습니다.
이렇게 reselector
라이브러리
의 createSelector
를 사용하면., 전과는 다르게 값에 아무런 변화가 없어도 filter()
와 slice()
는 실행되지 않고 이전의 연산된 값을 재사용한다는 것이 중요합니다.
'React' 카테고리의 다른 글
리액트의 Switch는 언제 사용할까? (0) | 2021.03.17 |
---|---|
제너레이터(Generator) (0) | 2021.02.23 |
react-redux의 shallowEqual 사용하기 (0) | 2021.02.22 |
redux-saga (0) | 2021.02.21 |
CORS와 Webpack DevSercer Proxy (0) | 2021.02.18 |