store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';
const user = createSlice({
name: 'user',
initialState: 'kim',
reducers: {
changeName(state) {
return `john ${state}`;
},
},
});
export const { changeName } = user.actions;
const cartItem = createSlice({
name: 'cartItem',
initialState: [
{ id: 0, name: 'White and Black', count: 2 },
{ id: 2, name: 'Grey Yordan', count: 1 },
],
});
export default configureStore({
reducer: {
user: user.reducer,
cartItem: cartItem.reducer,
},
});
위의 예제와 같이 간단하게 스토어와 슬라이스를 설정해두고, 이를 해당 컴포넌트에서 불러오기 위해
const stateGroup = useSelector((state) => state);
위와 같이 작성해보면, 아래와 같은 경고가 발생한다.
Selector unknown returned the root state when called. This can lead to unnecessary rerenders.
Selectors that return the entire state are almost certainly a mistake, as they will cause a rerender whenever anything in state changes.
{stack: 'Error\n at http://localhost:3000/static/js/bundl…tp://localhost:3000/static/js/bundle.js:33570:22)'}
Cart @ Cart.jsx:6
Show 20 more frames
위의 오류 메시지는 useSelector 훅이 전체 상태를 반환하는 경우 발생할 수 있는 문제를 경고하고 있다.
이렇게 하면 상태의 일부가 변경될 때마다 컴포넌트가 다시 렌더링되기 때문에 비효율적이라고 말한다.
이를 해결하기 위해 combineReducers를 생각해보았는데, 이미 configureStore 내부에서 자동으로 처리되기 때문에 별도로 할 필요는 없다고 한다.
const cartItem = useSelector((state) => state.cartItem);
const user = useSelector((state) => state.user);
이렇게 필요한 상태만 선택하여 사용해야 하나...?
여러 상태를 한 번에 가져오고 싶다면, useSelector 훅을 여러 번 호출하는 대신, 한 번의 호출로 필요한 상태들을 묶어서 가져오는 방식이 무엇일까?
구조 분해를 사용하여 가져와야 하나...
const { cartItem, user } = useSelector((state) => ({
cartItem: state.cartItem,
user: state.user,
}));
위와 같이 작성할 경우 아래와 같은 에러를 볼 수 있다.
Selector unknown returned a different result when called with the same parameters. This can lead to unnecessary rerenders.Selectors that return a new reference (such as an object or an array) should be memoized: https://redux.js.org/usage/deriving-data-selectors#optimizing-selectors-with-memoization {state: {…}, selected: {…}, selected2: {…}, stack: 'Error\n at http://localhost:3000/static/js/bundl…tp://localhost:3000/static/js/bundle.js:33576:22)'} at Cart (http://localhost:3000/main.5a25427396412b3edf33.hot-update.js:32:63) at RenderedRoute (http://localhost:3000/static/js/bundle.js:50711:5) at Outlet (http://localhost:3000/static/js/bundle.js:51315:26) at Root at RenderedRoute (http://localhost:3000/static/js/bundle.js:50711:5) at RenderErrorBoundary (http://localhost:3000/static/js/bundle.js:50658:5) at DataRoutes (http://localhost:3000/static/js/bundle.js:49278:5) at Router (http://localhost:3000/static/js/bundle.js:51336:15) at RouterProvider (http://localhost:3000/static/js/bundle.js:49065:5) at Provider (http://localhost:3000/static/js/bundle.js:46149:3)
에러 메시지를 보면, useSelector 훅이 반환하는 객체가 매 렌더링마다 새로운 참조를 가지기 때문에 발생하는 문제로 보인다. 이를 해결하기 위해 알아보면 reselect 라이브러리를 사용해 셀렉터를 메모이제이션하는 방법을 고려해볼 수 있다.
npm install reselect
import { createSelector } from 'reselect';
const selectCartItem = (state) => state.cartItem;
const selectUser = (state) => state.user;
export const selectCartAndUser = createSelector(
[selectCartItem, selectUser],
(cartItem, user) => ({
cartItem,
user,
})
);
const { cartItem, user } = useSelector(selectCartAndUser);
'React' 카테고리의 다른 글
리액트의 동작원리 TEST (0) | 2024.07.02 |
---|---|
리덕스(Redux) & 리덕스 툴킷(Redux Toolkit) (0) | 2024.06.26 |
useLayoutEffect + useEffect, useTransition, useDeferredValue (0) | 2024.06.20 |
useState 사용할 때 함수설정 (0) | 2024.06.17 |
[Webpack]를 수동으로 설정해보기 (3) (0) | 2024.06.17 |