상태 관리 라이브러리 - Recoil (3. Selector)

eeensu·2023년 8월 5일
0
post-thumbnail

Selector

Selector는 atoms나 다른 selectors를 입력으로 받아들이는 순수함수이다. 파생된 상태의 일부를 나타내며 파생된 상태를 어떤 방법으로든 주어진 상태를 수정하는 순수 함수에 전달된 상태의 결과물이다.

파생된 상태는 다른 데이터에 의존하는 동적인 데이터를 만들 수 있기 때문에 효율적이다.
Selectors는 상태를 기반으로 하는 파생 데이터를 계산하는 데 사용하고, 이를 selectors에 명시된 함수를 통해 효율적으로 계산함으로써 쓸모없는 상태의 보존을 방지한다.




예시로, todoList에서 오늘의 할일 목록을 atom으로 지정할 수 있으면,
오늘의 todo 완료 개수, 남은 todo 백분율값 등등의 계산을 통한 값을 표현하는 데에 사용한다.

  1. selector 함수를 작성
    todoStore.ts 파일에 똑같이 selector를 작성해준다. 내용은 완료한 todo의 개수이다.
export const todoCompleteCountState: RecoilValueReadOnly<number> = selector({
    key: 'todoCompleteCountState',
    get: ({ get }) => {
     	// 기존의 todoListState를 가져와서 계산한다.
        const todoList: Todo[] = get(todoListState);	
      
      	// 가져온 todoList에서 완료된 것들만 필터링하여 돌려준다.
        const todoCount = todoList.filter((todo, i) => todo.isComplete);
		
        // 필터링된 배열의 길이를 리턴하며 완료한 todo 목록의 갯수를 돌려준다.
        return todoCount.length;        
    }
});

selector 역시 atom과 동일하게 고유한 key를 가져야 한다. 또한, selector는 계산된 값만 가져오는 역할이기에 가져올 값을 계산하는 로직을 get 안에 작성해준다. get은 콜백함수를 받아올 수 있고, 안에는 get 이라는 또 다른 속성이 있는데, 이는 기존의 state를 가져올 수있다.


  1. 계산된 값을 컴포넌트에서 사용
    계산된 값을 가져오는 hook은 useRecoilValue() 로 atom과 동일하다. 기존의 todoList에 완료한 목록까지 보여주는 컴포넌트로 만들었다.
const TodoList: FC = () => {

    const todoList = useRecoilValue<Todo[]>(todoListState);
    const completeCount = useRecoilValue<number>(todoCompleteCountState);
    
    return (
        <div>
            <p>완료한 수 : {completeCount} / {todoList.length}</p>
            {todoList.map((todo) => (
              <TodoItem 
                key={todo.id} 
                id={todo.id} 
                task={todo.task} 
                isComplete={todo.isComplete}
                />
            ))}
        </div>
    );
};

export default TodoList;




활용

다음과 같이 selector를 활용하여 여러 계산된 값을 제작할 수 있다.

  • 모두 보기 / 완료 todo 보기 / 미완료 todo 보기
// 보여주는 필터의 타입을 설정
export type ShowType = 'SHOW ALL' | 'ISDONE' | 'ISNOTDONE';

// 기존 todoList의 state
export const todoListState: RecoilState<Todo[]> = atom({
    key: 'todoListState',
    default: [] as Todo[]
});

// 필터된 todoList의 state
export const todoListFilterState: RecoilState<ShowType> = atom({
    key: 'todoListFilterState',
    default: 'SHOW ALL' as ShowType,
});

// 필터링된 todoList를 보여주는 selector
export const filteredTodoListState = selector({
    key: 'filteredTodoLisState',
    get: ({ get }) => {
        const filter = get(todoListFilterState);
        const list = get(todoListState);
        
        switch(filter) {
            case 'SHOW ALL': 
                return list;
            
            case 'IS_DONE': 
                return list.filter(todo => todo.isDone);

            case 'IS_NOT_DONE':
                return list.filter(todo => !todo.isDone);
        }
    }
});

  • 종합적인 todoList의 정보들을 객체 형태로 반환해주는 형식
export const todoListStats = selector({
    key: 'todoListStats',
    get: ({ get }) => {        
        const todoList = get(todoListState);
      
        const totalCount = todoList.length;
        const isDoneCount = todoList.filter(todo => todo.isDone).length; 
        const isNotDoneCount = todoList.filter(todo => !todo.isDone).length; 
        const percent = totalCount === 0 ? 0 : isDoneCount / totalCount;

        return {
            totalCount,
            isDoneCount,
            isNotDoneCount,
            percent
        };
    }
})
profile
안녕하세요! 26살 프론트엔드 개발자입니다! (2024/03 ~)

0개의 댓글