react-redux의 shallowEqual 사용하기

2021년 02월 22일 by Xion

    react-redux의 shallowEqual 사용하기 목차

| react-redux

Provider

react-redux 는 root-container 에서 Provider 를 사용하여 store 값을 할당해주면 됩니다.
Provider components 에서는 리액트에서 action 이 처리 됐을 때, 이벤트를 받아서 하위에 있는 다른 컴포넌트가
다시 렌더링 될 수 있도록 도와주는 역할을 합니다.

활용 예시

import { Provider } from 'react-redux';

//store를 가져옵니다 
//여기서 첫 번째 인자인 reducer는 combineReducers()등을 구현한 1개 이상의 리듀서들을 뜻합니다.
store = createStore(reducer,window.__REDUX_DEVTOOLS_EXTENSION___?.());

export default App (){
    <Provier store  = {store}
        <div>
            <...>
        </div>
    </Provider>
}

기존 FriendMain.js code

import { useEffect, useReducer } from "react";
import { getNextFriend } from "../../common/mockData";
import store from "../../common/store";
import FriendList from "../component/FriendList";
import { addFriend } from "../state";

//FriendMain
function FriendMain() {

    const [, forceUpdate] = useReducer(v => v + 1, 0);


    //딱 한 번만 로딩 되도록
    useEffect(() => {
        //전에 데이터 friends
        let prevFriends = store.getState().friend.friend;

        //unsubscribe 함수는 dispatch 함수를 실행 시키는 함수
        //store값이 변하는지 계속 관찰함.
        const unsubscribe = store.subscribe(() => {

            //현재 friends
            const friends = store.getState().friend.friend;

            //전 friends와 현재 friends가 같지 않다면
            //즉 변화가 발생하면 렌더링
            if (prevFriends !== friends) {
                //렌더링 실행
                forceUpdate();
            }

            //그 후 prevFriends 값을 현재 friends 배열로 update
            prevFriends = friends;
        });

        return unsubscribe;
    }, []);

    function onAdd() {
        const friends = getNextFriend();
        store.dispatch(addFriend(friends));
    }

    const friendList = store.getState().friend.friend;


    return (
        <div>
            <button onClick={onAdd} >친구 리스트 추가</button>
            <FriendList friends={friendList} />
        </div>
    )
};


export default FriendMain;

해당 코드에서는 이전 data 값과 비교를 위한 변수가 필요했었습니다.
( 이유 : 불필요한 렌더링을 막기 위하여 비교를 했었음 )

useSelector Hook 사용

useSelector

위의 코드에서 react-redux를 사용한다면 다음과 같은 코드로 update 됩니다.

바로 ! useSelector()useDispatch() Hook을 사용하면 됩니다.

 

업데이트 된 FriendMain.js code

//useSelector를 사용하여 현재 상태값에서 가져올 data를 선택합니다.
const friends = useSelector(state => state.friends.friends);

위와 같이 만들어주면 store.getState() 를 사용하여 현재 데이터를 가져올 필요가 없이
useSelector() 만으로 가져올 수 있습니다.

 

 

여러개의 state 값을 가져오고 싶다면 ?

1.useSelector를 여러번 사용하기

const e1 = useSelector(state => ... ) 
const e2 = useSelector(state => ... )
const e3 = useSelector(state => ... )

위와 같은 방식으로 선언하여 사용해주면 됩니다.
하지만 너무 보기 안 좋죠?

 

2.조금 개선된 방법인 배열, 객체 형태로 받기

export default function FriendMain(){
//배열로 여러개의 state 값 받기
const [friends,friends2] =  useSelector(state => [state.friend.friends, state.friend.friends2]);
//객체로 여러개의 state 값 받기
const {friends, name } =  useSelector(state => {state.friend.friends, state.friend.friends2.name });

    ...
}

하지만 이 방법에도 문제가 조금 존재합니다.

배열이 매번 생성이 되기 때문에 안에 있는 값들이 변경되지 않더라도, redux에서 action이 처리될 때 마다 불필요하게 FriendMain 컴포넌트가 랜더링될 수 있다는 단점이 존재합니다.

그럼 어떻게 해야할까?

바로 shallowEqual 함수를 두 번째 인자로 넣어줍니다.

// 두 번째 인사로 shallowEqual 넣기
const[friends, friends2] = useSelector(state => [state,friend.friends, state.friend.friends2], shallowEqual);

shallowEqual 함수는 얕은 비교를 하기 때문에 배열을 입력했을 때 배열의 레퍼런스만 비교하는 것이 아니라 안에있는 friends와 friends2를 각각 비교 하기 때문에 해당 값이 변경되었을 때만 컴포넌트가 렌더링될 것 입니다.

하지만 매번 입력하기도 귀찮기 때문에

커스텀 Hook 을 만들어 사용하면 편리합니다.

shallowEuqal을 위한 custom Hook

커스텀 hook

function useMySelector(selector){
    //useSelector 훅과 shallowEqual을 사용한 것을 return해줍니다.
    return useSelector(selector,shallowEqual);
}

function MyCompoenent(){

    //위에서 선언한 useMySelector 훅으로 첫 번째 인자로 (state=> ... ) 들을 전달해주면
    //반환값으로는 shallowEqual을 사용하여 비교한 값으로 return해줍니다.
    const[value1, value2] = useMySelector(state => [state.value1,state.value2];
    const value3 = useMySelector(state=> state.value3);
    const [value4] = useMySelector(state => [state.value4]);
}

여기서 주의할 점은 배열을 입력하지 않고 사용을 했을 때, (2번째 value3 의 경우)

useMySelector(state => state.value3);

배열을 입력하지 않고 위와 같이 코드를 사용 할 때에는, value3의 모든 속성값을 비교하기 때문에 성능상 비효율적일 수 있습니다.
따라서 값을 하나만 반환 하더라도 "[]" 배열로 감싸서 반환해야합니다.

'React' 카테고리의 다른 글

제너레이터(Generator)  (0) 2021.02.23
리덕스 reselect 사용하기  (0) 2021.02.22
redux-saga  (0) 2021.02.21
CORS와 Webpack DevSercer Proxy  (0) 2021.02.18
redux-thunk란?  (0) 2021.02.18