To Do List #3 Recoil Selector

Leesu·2022년 11월 30일
0

Recoil Selector

Selector는 파생된 state(derived state)의 일부를 나타낸다.
즉, 기존 state를 가져와서, 기존 state를 이용해 순수함수를 거쳐 새로운 state를 만들어서 반환할 수 있다.
(selector 는 atom 의 기존 state 를 사용해 output 만을 변형시키는 도구)
기존 state를 이용만할 뿐 변형시키지 않는다. derived state는 다른 데이터에 의존하는 동적인 데이터를 만들 수 있기 때문에 강력한 개념이다.

  • 사용 방법
const filteredTodoListState = selector({
  key: 'filteredTodoListState',
  get: ({get}) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);

Selector 미사용

  • 우선, selector 를 사용하지 않는 상태의 예시를 봐보자.
    • category 3개를 분류해줄 건데,
      아래는 toDo 의 카테고리가 "DOING" 이 아닐때만, "Doing" 버튼을 보여주는 코드다.
- ToDo.tsx

function ToDo({ text, category, id } : IToDo) {
    const onClick = (event: React.MouseEvent<HTMLButtonElement>) => {
      const {
        currentTarget: { name },
      } = event;
      setToDos((oldToDos) => {
            const targetIndex = oldToDos.findIndex((toDo) => toDo.id === id);
            							// toDo 의 id 와 props 에서 오는 id 비교
            
             const newToDo = { text, id, category: name as any };
              return [
                ...oldToDos.slice(0, targetIndex),
                newToDo,
                ...oldToDos.slice(targetIndex + 1),
              ];
          });
    };
	return (
      {category !== "DOING" && (
        <button name="DOING" onClick={onClick}>
          Doing
        </button>
      )}
      {category !== "TO_DO" && (
        <button name="TO_DO" onClick={onClick}>
          To Do
        </button>
      )}
      {category !== "DONE" && (
        <button name="DONE" onClick={onClick}>
          Done
        </button>
      )}
	)
}
  • toDo 의 인덱스를 찾아서 저장하고, 해당 인덱스의 category 값을 업데이트(변경) 해주기 위해
    찾아낸 인덱스 값 targetIndex 를 사용해, oldToDos 사이에 newToDo 를 추가했다.
- atomos.tsx

export interface IToDo {
  text: string;
  category: Categories;
  id: number;
}

export const toDostate = atom<IToDo[]>({
  key: "toDo",
  default: [],
});

Selector 사용

  • 기존 atoms.tsx 를 보면 "DOING" 이던, "DONE" 이든.. category 가 한데 모여있다.
    이에 Selector 를 사용해 category 를 분류.
- atoms.tsx

import { atom, selector } from "recoil";

export const toDoSelector = selector({
  key: "toDoSelector",
  get: ({ get }) => {
    const toDos = get(toDoState);
      // → get Fn : Selector 내부에 atom 을 가져올 수 있다.
    return [
      // → toDoSelector 의 value 값.
      toDos.filter((toDo) => toDo.category === "TO_DO"),
        // → filter fn : 배열에서 조건에 맞지 않는 원소들을 제거한 배열을 return 
      toDos.filter((toDo) => toDo.category === "DOING"),
      toDos.filter((toDo) => toDo.category === "DONE"),
    ];
  },
});
  • METHOD get

    • https://recoiljs.org/ko/docs/api-reference/core/selector/

      파생된 상태의 값을 평가하는 함수. 값을 직접 반환하거나 비동기적인 Promise나 또는 같은 유형을 나타내는 다른 atom이나 selector를 반환할 수 있다.
      첫 번째 매개변수로 다음 속성을 포함하는 객체를 전달한다.

    • OPTION get
      • 다른 atom이나 selector로부터 값을 찾는데 사용되는 함수.
      • get 에서 return 하는 값이 toDoSelectorvalue가 된다.
  • 위와 같이 작성해서 console 에서 확인할 경우

  • 위와같이 selector 의 output 이 세개의 빈 배열을 담은 배열로 결과를 확인할 수 있다!
-- ToDoList.tsx

function ToDoList() {
	const [toDo, doing, done] = useRecoilValue(toDoSelector);
      // → toDoSelector 배열 안에 있는 배열을 꺼내기 위함  
    return (
    	<div>
          <h1>To Dos</h1>
          <hr />
          <CreateToDo />
          <h2>To Do</h2>
          <ul>
             <ToDo key={toDo.id} {...toDo} />
        	))}
      	  </ul>
         <hr />
        <h2>Doing</h2>
        <ul>
          {doing.map((toDo) => (
            <ToDo key={toDo.id} {...toDo} />
          ))}
        </ul>
        <hr />
        <h2>Done</h2>
        <ul>
          {done.map((toDo) => (
            <ToDo key={toDo.id} {...toDo} />
          ))}
        </ul>
        <hr />
    </div>
  );
}
  • todo, doing, done 각각 카테고리를 갖고, 내가 category 값을 변경하면 그에 맞는 위치로 이동한다!

  • toDoState atom 배열에 모든 toDo를 넣는 로직은 변하지 않았지만,
    'selecotor' 에서 'atom' 을 받아서 'atom' 의 'output' 을 변형했기에
    이제 atom 에서 값을 가져오지 않고, 'selector' 에서 값을 받게되었다.

  • 이제, 지금처럼 한 화면에 todo, doing, done 을 표시하지말고,
    seclet + option 태그를 사용해 선택한 category 와 todo 만 화면에 표시해보자
    즉, 한번에 하나의 카테고리만 ㅇㅅㅇ

  • 그러기 위해 새로운 state 를 만들고, 기본 category 를 "TO_DO"로 변경.
    selector 에서도 카테고리에 따른 한 개만 화면에 렌더링 되게 return 값을 변경했다.

-- atoms.tsx

export const categoryState = atom({
  key: "category",
  default: "TO_DO",
});

export const toDoSelector = selector({
  key: "toDoSelector",
  get: ({ get }) => {
    const toDos = get(toDoState);
	const category = get(categoryState);
    return toDos.filter((toDo) => toDo.category === category);
  },
});
-- ToDoList.tsx

function ToDoList() {
    const toDos = useRecoilValue(toDoSelector);
      const [category, setCategory] = useRecoilState(categoryState);
      const onInput = (event: React.FormEvent<HTMLSelectElement>) => {
        setCategory(event.currentTarget.value);
    };
    return (
      <div>
        <h1>To Dos</h1>
        <hr />
        <select value={category} onInput={onInput}>
          <option value="TO_DO">To Do</option>
          <option value="DOING">Doing</option>
          <option value="DONE">Done</option>
        </select>
        <CreateToDo />
        {toDos?.map((toDo) => ( <ToDo key={toDo.id} {...toDo} /> ))}
     </div>
  );
}
  • selector 가 toDos 랑 category 를 받아서 cateogry 에 따라서 toDo 를 분류해준다!


  • Array.prototype.findIndex()

    • findIndex() 메서드는 주어진 판별 함수를 만족하는 배열의 첫 번째 요소에 대한 인덱스를 반환합니다. 만족하는 요소가 없으면 -1을 반환합니다.

      const array1 = [5, 12, 8, 130, 44];
      
      const isLargeNumber = (element) => element > 13;
      
      console.log(array1.findIndex(isLargeNumber));
      // expected output: 3
profile
기억력 안 좋은 FE 개발자의 메모장

0개의 댓글