처음 만난 리덕스 (Redux) 문서


15.1 useSelector()

자, 그럼 Redux와 관련된 훅에 대해서 본격적으로 배워볼까요?
가장 먼저 나오는 훅은 useSelector() 훅입니다.

useSelector() 훅은 이름이 가진 의미 그대로, Redux Store의 state로부터 원하는 데이터만 선택할 수 있게 해주는 훅입니다.

Redux State Tree with hook

앞에서 살펴본 것처럼 Redux Store는 이렇게 Tree 형태로 구성되어 있습니다.

하지만 리액트 컴포넌트에서 항상 State Tree에 있는 모든 데이터가 필요한 것은 아니죠.
그래서 여기서 일부 데이터만을 가져와서 사용해야 하는데, 이 때 사용하는 것이 바로 useSelector() 훅입니다.
위 그림에 빨간색으로 표시한 부분처럼 State Tree에서 원하는 부분만 선택해서 가져올 수 있게 해주는 것이죠.

useSelector() 훅의 사용 방법은 다음과 같습니다.

const partialState = useSelector(selectorFunction, equalityFunction?);

먼저 첫 번째 파라미터로 selectorFunction이 들어갑니다.
selectorFunction은 Root State를 파라미터로 받아서 추출하기 원하는 State만을 선택하는 함수입니다.

이 때 한 가지 기억해야 할 점은, selectorFunction은 무조건 pure 함수여야 한다는 점입니다.
즉, selectorFunction은 입력으로 받은 파라미터를 변경하지 않으며, 항상 같은 입력에 대해 같은 출력을 리턴해야 한다는 것이죠.

useSelector() 훅은 리액트 컴포넌트 내에서 여러번 복합적으로 호출될 수 있기 때문에, 만약 selectorFunction이 pure 함수가 아니라면 예상치 못한 동작이 발생할 수 있습니다.
그렇기 때문에 selectorFunction을 꼭 Pure 함수로 작성해야 한다는 것입니다.

그리고 두 번째 optional 파라미터로는 equalityFunction이 들어가는데, equalityFunction은 선택한 state가 동일한지 아니면 변경되었는지 여부를 리턴하는 함수입니다.
만약 equalityFunction이 state가 변경되었다고 리턴하게 되면 컴포넌트가 재렌더링 됩니다.

equalityFunction은 optional 파라미터이기 때문에 생략하게 되면, 기본적으로는 strict reference equality checks를 수행하게 됩니다.
즉, 새로운 배열이나 객체가 생성된 것이 아니면 state가 변하지 않았다고 판단하게 되는 것이죠.

개발자가 원하는 특정 상황에서 state가 변경되었다고 리턴하게 하려면 이 equalityFunction을 직접 작성해서 넣으면 됩니다.

import { useSelector } from 'react-redux';

function Counter(props) {
    const count = useSelector((state) => state.count);

    return <div>{`현재 카운트: ${count}`}</div>;
}

export default Counter;

위 코드는 useSelector() 훅을 실제로 사용하는 예시 코드입니다.

이 부분에서 useSelector() 훅을 사용해서 statecount 값만을 가져오고 있는 것을 볼 수 있습니다.
그리고 이렇게 가져온 count 값을 화면에 출력하고 있습니다.

굉장히 단순하죠?
그럼 이번에는 조금 더 복잡한 예시 코드를 보도록 하겠습니다.

import { useSelector } from 'react-redux';

function Post(props) {
    const post = useSelector((state) => state.posts[props.postId]);

    return (
        <div>
            <h1>{post.title}</h1>
            <p>{post.content}</p>
        </div>
    );
}

export default Post;

이 코드도 역시 useSelector() 훅을 사용한 예시 코드인데, 여기에서는 컴포넌트의 props를 사용해서 Redux Root State로부터 특정 post만을 가져오고 있습니다.

이런 형태로 props를 사용해서 useSelector() 훅에서 원하는 state만을 선택해서 가져올 수 있습니다.

function mapStateToProps(state, ownProps) {
    const { posts, comments } = state;

    const targetPost = posts.find((post) => {
        return post.id === ownProps.postId;
    });

    return {
        post: targetPost,
    }
}

지금까지 useSelector() 훅을 살펴보면서 이미 인지한 분도 계시겠지만, useSelector() 훅은 앞에서 Container를 만들 때 사용했던 connect() 함수의 mapStateToProps() 함수와 동일한 역할을 합니다.
mapStateToProps() 함수도 전체 State로부터 일부 state만을 추출해서 사용할 수 있게 해주었기 때문이죠.

하지만 useSelector() 훅과 mapStateToProps() 함수는 몇 가지 차이점이 있습니다.

먼저 mapStateToProps() 함수는 객체를 리턴해야 했지만, useSelector() 훅에서는 객체가 아닌 다른 타입의 변수들도 리턴할 수 있습니다.
그리고 그렇게 리턴된 값을 곧바로 컴포넌트에서 사용할 수 있었죠.

그리고 mapStateToProps() 함수는 ownProps라는 속성을 통해서 컴포넌트의 props에 접근할 수 있었지만, useSelector() 훅은 애초에 함수 컴포넌트 내에 위치하기 때문에 컴포넌트의 props에 곧바로 접근할 수 있습니다.

마지막으로 Action이 Dispatch되면 useSelector() 훅은 이전 useSelector() 훅의 결과 값과 현재 값을 비교하여, 만약 값이 변경되었다면 컴포넌트를 강제로 재렌더링 시킵니다.
mapStateToProps() 함수를 사용하는 방식에서는 stateprops로 전달되기 때문에, props로 전달되는 state가 변경되면 컴포넌트가 자연스럽게 재렌더링 되었는데, 그 방식과는 조금 차이가 있다고 볼 수 있습니다.


마지막 업데이트: 2023년 07월 14일 00시 00분

이 문서의 저작권은 이인제(소플)에 있습니다. 무단 전재와 무단 복제를 금합니다.