[#RTK] createSelector 이해하기

calm·2022년 5월 18일
0

개인적인 생각과 발견으로 작성한 글이라, 오류가 있을 수 있습니다. 문제 있는 부분은 답글로 추가 설명 부탁드립니다.

createSeletor 예시코드

export const selectPostsByUser = createSelector([selectAllPosts, (state,userId)=> userId],(posts, userId)=> posts.filter((post)=> post.user ===userId)
//selectAllPosts
export const selectAllPosts = (state)=> state.posts.posts

//userId
user의 Id

useSelector 란,

리덕스 store는 애플리케이션 컴포넌트 각자의 state가 모아진, 총 집합체의 그것이다.
작업자는 store 중에 필요한 state를 사용해야 할 경우가 있는데, 이때 'useSelector'함수를 통해서 관심되는 state를 가져(사용 할 수) 올 수 있다,

const user = useSelector((state) => state.user)

useSelector함수는 애플리케이션의 전체 상태(state)를 인자로 받아서, 해당 컴포넌트에서 사용하려는 user state를 반환한다.

createSelector 란,

useSelector를 사용하기 전(=컴포넌트에서 값을 가져와 사용하기 전), 상태에 처리작업(예:fileter)이 필요할 경우, 처리작업을 미리 적용 후, 이를 메모이제이션하는 helper 함수,

단지,컴포넌트에서는 createSelector함수가 반환한 state만 useSelector에서 불러와 사용하면 된다.

컴포넌트가 상태를 처리하는 작업은, 컴포넌트가 리렌더링 되는 문제를 야기할 수 있다. 이런 문제를 미연에 방지하기 위해, 상태 변경작업을 리덕스에서 미리 처리해둬서 컴포넌트에서는 그냥 가져다 쓰게끔 도와주는 함수

함수도 객체의 일종으로, 상태가 변경되면, 컴포넌트가 리렌더링 되면서, 함수도 새로생성, 즉 객체도 새로 생성되는 것과 같아, 애플리케이션의 자원을 소비하는데, 메모이제이션 기능을 더해서 애플리케이션 퍼포먼스를 올려주는 헬퍼함수.

createSelector를 왜 사용할까?

1.컴포넌트의 re-rendering 때문,
2.useSelector를 통해 가져온 state를 컴포넌트에서 어떤 작업을 하게 되면, 그 state가 변경되고, 변경된 state로 인해 컴포넌트가 리렌더링 되고, 연쇄작용 발생

state를 가져오는데 왜 컴포넌트가 리렌더링 될까,

useSelector로 가져온 state를 컴포넌트 내에서 사용할 경우, 처리과정에서 state가 변경될 수 있다. 이것은 컴포넌트의 리렌더링을 야기할 수 있고, 이미 진행된 로직(dispatch호출, useSelector호출)을 다시 실행하게 만든다. ,

즉, 어떤 처리과정을 통해서 새로운 객체가 생성되며, 새로운 객체가 만들어지면, 리액트는 상태(state)가 달라졌다고 인식하고 컴포넌트를 리렌더링 하게 된다.

다시말해, 작업자는 state를 가져오는 것 밖에 하지 않았는데, 본의아니게 컴포넌트를 리렌더링하는 결과도 초래하게된다.

새로운 객체가 생성되다

리액트는 state/props가 변경이 되면 컴포넌트가 새롭게 re-rendering 된다. (여기에서는 state가 변경되는 경우다)

리액트(자바스크립트)가 갖는/참조하는 상태(state)란 객체이다.

객체는 객체 자신이 참조하는 값이 있는데, 새로 객체가 생성될때마다 그 값이 변경된다.

리액트는 객체의 참조값을 주시?하고 있기 때문에, 작업자가 사용하는 객체가 변경했는지 아닌지가 주 관심사 라고 생각된다.

그래서 객체를 복사하는 것, 객체를 변경하지 않는 것을 중요하게 생각하는 것 같다,

무엇이 새로운 객체를 만들고 있을까?

서두에 정리한 코드를 다시 확인해보자
(*변경 이전의 코드는 아래 내용에서 확인할 수 있어 제외합니다.)

export const selectPostsByUser = createSelector([selectAllPosts, (state,userId)=> userId],(posts, userId)=> posts.filter((post)=> post.user ===userId)
(posts, userId)=> posts.filter((post)=> post.user ===userId)

filter함수는, 각 배열요소가 filter에 전달한 콜백함수의 조건에 부합(filter)하는 것만 모아진 새로운 배열을 반환합니다.

즉, 새로운 배열이 생성됩니다.
새로운 객체가 생성되는 것이지요,

리액트는 새로운 객체가 생성된것을 감지?하고 컴포넌트를 새롭게 렌더링합니다.

createSelector는 state을 메모이제이션 하게 가져오는 방법이라고 생각합니다.

왜 메모이제이션을 할까?

컴포넌트가 상태가 변경되지 않음을 알려주기 위해서 라고 생각한다.

컴포넌트에서 사용되는 상태는 언제나 동일해야 하는데, 리덕스 스토어에서 가져오는 상태가 매번 달라진다면, 이유없이 새롭게 렌더링이 발생하기 때문이다.

createSelector구조 이해하기

createSelector('a','b')

a: b에 사용될 데이터(디펜던시)
b: a 데이터를 가공/처리하는 함수

export const selectPostsByUser = createSelector([selectAllPosts, (state,userId)=> userId],(posts, userId)=> posts.filter((post)=> post.user ===userId)

'b에 사용될 데이터'

[selectAllPosts, (state,userId)=> userId]

단일한 값(객체,배열)형태가 아니어도, 함수를 전달해서 두번째 인수에 들어갈 데이터를 반환하는 것이 들어간다, 데이터를 반환하는 함수가 들어간다.

a 데이터를 가공/처리하는 함수

(posts, userId)=> posts.filter((post)=> post.user ===userId)

반환된 데이터로 가공하는 함수를 넣는다,
여기에서는 앞에 인자에서 posts,userId을 가지고 있다.
이 posts, userId를 가지고 fileter 작업을 진행한다.

profile
공부한 내용을 기록합니다

0개의 댓글