Atom은 상태(state)의 일부를 나타낸다. Atoms는 어떤 컴포넌트에서나 읽고 쓸 수 있다. atom의 값을 읽는 컴포넌트들은 암묵적으로 atom을 구독한다. 그래서 atom에 어떤 변화가 있으면 그 atom을 구독하는 모든 컴포넌트가 재 렌더링 되는 결과가 발생할 것이다.
리코일은 리액트 자체의 기능이 아니며, 리액트 애플리케이션을 위한 상태 관리 라이브러리입니다. 페이스북이 개발하고 유지 관리하는 리액트는 리액트 개발자들이 이미 알고 있는 후크와 같은 기본 요소를 사용하여 리액트 앱에서 글로벌 상태를 보다 리액트와 유사한 방식으로 관리할 수 있는 방법을 제공합니다. 특히 대규모 애플리케이션에서 복잡한 상태를 관리하는 데 있어 리덕스나 MobX와 같은 다른 라이브러리에 비해 상태 관리 솔루션의 단순성, 미니멀리즘 및 더 나은 성능을 제공하는 것을 목표로 합니다.
원자: 원자는 상태의 단위입니다. 각 원자는 고유한 ID를 가지고 있으며 구성 요소별로 가입할 수 있습니다. 원자의 상태가 변경되면 해당 원자에 가입한 모든 구성 요소가 새로운 상태로 다시 렌더링됩니다. 원자는 앱 어디에서나 사용할 수 있으며 구성 요소 간에 상태를 공유할 수 있는 방법을 제공합니다.
Selector: Selector는 원자 또는 다른 Selector를 기반으로 상태를 도출하는 순수 함수입니다. 이를 통해 기본 원자가 변경될 때마다 업데이트되는 파생 상태를 만들 수 있습니다. Selector는 계산을 수행하고 결과 값을 반환할 수 있으므로 응용 프로그램에서 파생 데이터를 관리하는 강력한 도구가 됩니다.
Hooks(후크): Recoil(반동)은 로컬 상태 관리에 React(리액트)의 내장 후크를 사용하는 것과 유사하게 RecoilState(반동 상태) 사용, RecoilValue(반동 값) 사용, SetRecoilState(반동 상태) 사용 등과 같은 여러 사용자 지정 후크를 도입합니다.
동시 모드 호환성: 리코일은 리액트의 동시 모드와 호환되도록 설계되어 비동기 렌더링을 처리하는 리액트의 새로운 기능을 활용하여 더 나은 성능과 사용자 경험을 제공합니다.
반동 사용 시기:
Recoil은 여러 구성 요소를 공유해야 하는 복잡한 상태 논리를 가지고 있고, 그러한 상태를 관리할 수 있는 보다 직관적이고 React와 유사한 방법을 찾고 있는 시나리오에서 빛을 발합니다. 다음은 Recoil이 특히 유용할 수 있는 몇 가지 상황입니다:
대규모 반응 응용 프로그램: 응용 프로그램이 증가함에 따라 상태 관리가 점점 더 복잡해질 수 있습니다. Recoil의 글로벌 상태 관리 시스템은 많은 구성 요소 간의 상태 공유 및 업데이트를 단순화하는 데 도움이 될 수 있습니다.
복잡한 상태 로직: 비동기 데이터 페치, 캐싱 등을 포함하여 복잡한 상태 로직을 포함하는 애플리케이션의 경우 Recoil의 원자 및 선택기를 사용하여 상태를 효율적으로 관리할 수 있는 구조화된 방법을 제공합니다.
성능 문제: 성능에 민감한 애플리케이션을 작업하는 경우, 리코일의 최적화된 렌더링과 리액트의 동시 모드와의 호환성을 통해 불필요한 렌더링을 줄이고 사용자 환경을 개선할 수 있습니다.
// useTodosStatus 훅 정의: TODO 항목들의 상태
//와 관련된 여러 기능을 제공합니다.
function useTodosStatus() {
// Recoil 상태를 사용하여 TODO 목록을 관리합니다. Recoil을 사용하는 이유는 글로벌 상태 관리의 편의성과 컴포넌트 간 상태 공유의 용이성 때문입니다.
const [todos, setTodos] = useRecoilState(todosAtom);
// 마지막 TODO 항목의 ID를 관리하는 Recoil 상태입니다. 각 TODO 항목에 고유 ID를 부여하기 위해 사용됩니다.
const [lastTodoId, setLastTodoId] = useRecoilState(lastTodoIdAtom);
// useRef를 사용하여 마지막 TODO ID의 현재 값을 저장합니다. useRef를 사용하는 이유는 컴포넌트 리렌더링 시에도 이 값을 유지하기 위해서입니다.
// 또한, useRef는 .current 프로퍼티에 값을 직접 할당하여 변경할 수 있으며, 이 값의 변경이 리렌더링을 발생시키지 않습니다.
const lastTodoIdRef = React.useRef(lastTodoId);
// useRef로 참조된 마지막 TODO ID를 최신 상태로 업데이트합니다.
lastTodoIdRef.current = lastTodoId;
// 새로운 TODO 항목을 추가하는 함수입니다. 새 항목에 고유한 ID를 부여하고, 이를 Recoil 상태에 업데이트합니다.
const addTodo = (newContent) => {
const id = ++lastTodoIdRef.current; // 고유 ID 생성
setLastTodoId(id); // 마지막 TODO ID를 업데이트
const newTodo = {
id,
content: newContent,
regDate: dateToStr(new Date()), // 현재 날짜를 문자열로 변환하여 등록 날짜로 사용
};
setTodos((todos) => [newTodo, ...todos]); // 새 TODO 항목을 목록에 추가
return id; // 새로운 항목의 ID를 반환
};
// 지정된 ID를 가진 TODO 항목을 삭제하는 함수입니다.
const removeTodo = (id) => {
const newTodos = todos.filter((todo) => todo.id != id); // 해당 ID를 제외한 항목들만 필터링
setTodos(newTodos); // 상태 업데이트
};
// 지정된 ID를 가진 TODO 항목의 내용을 수정하는 함수입니다. (버전 1)
const modifyTodo = (id, content) => {
const newTodos = todos.map((todo) => (todo.id != id ? todo : { ...todo, content }));
setTodos(newTodos); // 상태 업데이트
};
// 특정 인덱스에 위치한 TODO 항목의 내용을 수정하는 함수입니다. (버전 2)
const modifyTodoByIndex = (index, newContent) => {
const newTodos = todos.map((todo, _index) =>
_index != index ? todo : { ...todo, content: newContent },
);
setTodos(newTodos); // 상태 업데이트
};
// 지정된 ID를 가진 TODO 항목의 내용을 수정하는 함수입니다. (버전 2)
const modifyTodoById = (id, newContent) => {
const index = findTodoIndexById(id); // ID로 인덱스를 찾음
if (index == -1) {
return null; // 해당 ID의 항목이 없으면 null 반환
}
modifyTodoByIndex(index, newContent); // 인덱스를 기반으로 항목 수정
};
// 지정된 ID를 가진 TODO 항목의 인덱스를 찾는 함수입니다.
const findTodoIndexById = (id) => {
return todos.findIndex((todo) => todo.id == id);
};
// 지정된 ID를 가진 TODO 항목을 찾는 함수입니다.
const findTodoById = (id) => {
const index = findTodoIndexById(id);
if (index == -1) {
return null; // 해당 ID의 항목이 없으면 null 반환
}
return todos[index]; // 해당 항목 반환
};
// 이 훅이 반환하는 객체입니다. TODO 관련 작업을 수행할 수 있는 함수들을 제공합니다.
return {
todos,
addTodo,
removeTodo,
modifyTodo,
findTodoById,
modifyTodoById,
};
}
JSON.stringify -> 객체를 문장화
JSON.parse -> 문장화 된걸 다시 객체화
persistAtom -> 로컬스토리지에 기록이 남는다.
하이브리드 웹 앱, HTML, CSS, JS만으로도 만들 수 있다.