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
를 사용하지 않는 상태의 예시를 봐보자.- 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>
)}
)
}
targetIndex
를 사용해, oldToDos
사이에 newToDo
를 추가했다.- atomos.tsx
export interface IToDo {
text: string;
category: Categories;
id: number;
}
export const toDostate = atom<IToDo[]>({
key: "toDo",
default: [],
});
atoms.tsx
를 보면 "DOING" 이던, "DONE" 이든.. 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
파생된 상태의 값을 평가하는 함수. 값을 직접 반환하거나 비동기적인 Promise나 또는 같은 유형을 나타내는 다른 atom이나 selector를 반환할 수 있다.
첫 번째 매개변수로 다음 속성을 포함하는 객체를 전달한다.
get
get
에서 return
하는 값이 toDoSelector
의 value
가 된다.위와 같이 작성해서 console 에서 확인할 경우
-- 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>
);
}
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>
);
}
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