π Reference
π Index
μ€μΉ
λλ ν 리 ꡬ쑰
.next
api
components
βββ todoItem.tsx
βββ todoItemCreator.tsx
βββ todoList.tsx
βββ todoListFilters.tsx
βββ todoListStats.tsx
node_modules
pages
βββ _app.tsx
βββ index.tsx
public
states
βββ filteredTodoListState.ts
βββ todoListFilterState.ts
βββ todoListState.ts
βββ todoListStatsState.ts
styles
.eslintrc.json
.gitignore
.prettierrc.json
next-env.d.ts
next.config.js
package.json
README.md
tsconfig.json
yarn-error.log
yarn.lock
pages > _app.tsx
<RecoilRoot>
νκ·Έ μΆκ°
import type { AppProps } from 'next/app';
import { RecoilRoot } from 'recoil';
import '../styles/globals.css';
function MyApp({ Component, pageProps }: AppProps) {
return (
<>
<RecoilRoot>
This is _app.tsx
<Component {...pageProps} />
</RecoilRoot>
</>
);
}
export default MyApp;
μΈν°νμ΄μ€ μμ±
lib > client > interface.ts
export interface TodoList {
id: number;
text: string;
isComplete: boolean;
}
μ μ μν κ΄λ¦¬ μμ±
states
ν΄λ μμ μμ±todoListState.ts
import { atom } from 'recoil';
import { v1 } from 'uuid';
import { TodoList } from '../lib/client/interfaces';
export const todoListState = atom<TodoList[]>({
key: `TodoList-${v1()}`,
default: [{ id: 0, text: 'hello', isComplete: true }],
});
todoListFilterState.ts
import { atom } from 'recoil';
import { v1 } from 'uuid';
// Show All, Show Completed, Show Uncompleted
export const todoListFilterState = atom<string>({
key: `TodoListFilter-${v1()}`,
default: 'Show All',
});
todoListStatsState.ts
import { selector } from 'recoil';
import { v1 } from 'uuid';
import { todoListState } from './todoListState';
export const todoListStatsState = selector({
key: `TodoListStats-${v1()}`,
get: ({ get }) => {
const todoList = get(todoListState);
const totalNum = todoList.length;
const totalCompletedNum = todoList.filter((item) => item.isComplete).length;
const totalUncompletedNum = totalNum - totalCompletedNum;
const percentCompleted =
totalNum === 0 ? 0 : (totalCompletedNum / totalNum) * 100;
return {
totalNum,
totalCompletedNum,
totalUncompletedNum,
percentCompleted,
};
},
});
filteredTodoListState.ts
import { selector } from 'recoil';
import { v1 } from 'uuid';
import { todoListFilterState } from './todoListFilterState';
import { todoListState } from './todoListState';
export const filteredTodoListState = selector({
key: `FilteredTodoList-${v1()}`,
get: ({ get }) => {
const filter = get(todoListFilterState);
const list = get(todoListState);
switch (filter) {
case 'Show Completed':
return list.filter((item) => item.isComplete);
case 'Show Uncompleted':
return list.filter((item) => !item.isComplete);
default:
return list;
}
},
});
κΈ°λ₯ μ μ
μ»΄ν¬λνΈ μμ±
todoItem.tsx
import { ChangeEvent } from 'react';
import { useRecoilState } from 'recoil';
import { TodoList } from '../lib/client/interfaces';
import { todoListState } from '../states/todoListState';
export default function TodoItem({ item }: { item: TodoList }) {
const [todoList, setTodoList] = useRecoilState(todoListState);
const index = todoList.findIndex((listItem) => listItem === item);
const editItemText = (event: ChangeEvent<HTMLInputElement>) => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
text: event.target.value,
});
setTodoList(newList);
};
const toggleItemCompletion = () => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
isComplete: !item.isComplete,
});
setTodoList(newList);
};
const deleteItem = () => {
const newList = removeItemAtIndex(todoList, index);
setTodoList(newList);
};
return (
<div>
<input type="text" value={item.text} onChange={editItemText} />
<input
type="checkbox"
checked={item.isComplete}
onChange={toggleItemCompletion}
/>
<button onClick={deleteItem}>X</button>
</div>
);
}
function replaceItemAtIndex(
arr: TodoList[],
index: number,
newValue: TodoList,
) {
return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}
function removeItemAtIndex(arr: TodoList[], index: number) {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
}
todoItemCreator.tsx
import { ChangeEvent, useState } from 'react';
import { useRecoilState } from 'recoil';
import { todoListState } from '../states/todoListState';
export default function TodoItemCreator() {
const [inputValue, setInputValue] = useState('');
const [todoList, setTodoList] = useRecoilState(todoListState);
// utility for creating unique Id
function getId() {
return todoList.length > 0 ? todoList[todoList.length - 1].id + 1 : 0;
}
const addItem = () => {
setTodoList((oldTodoList) => [
...oldTodoList,
{
id: getId(),
text: inputValue,
isComplete: false,
},
]);
setInputValue('');
};
const onChange = (event: ChangeEvent<HTMLInputElement>): void => {
console.log('>>>> onChange:', event);
setInputValue(event.target.value);
};
return (
<div>
<input type="text" value={inputValue} onChange={onChange} />
<button onClick={addItem}>Add</button>
</div>
);
}