Recoil で Todo リストを作ってみます。公式チュートリアルをなぞります。
Atoms にはアプリケーションのステートを含んでいます。Todoリストのステートはオブジェクトの配列になり、各オブジェクトはTodoアイテムです。
atom() 関数で todo リストを表現してみます。
export const todoListState = atom({
key: "TodoList",
default: []
});
この atom の内容を useRecoilValue を使って read してみます。
function TodoList() {
const todoList = useRecoilValue(todoListState);
return (
<>
<TodoItemCreator />
{todoList.map((todoItem) => (
<TodoItem key={todoItem.id} item={todoItem} />
))}
</>
);
}
TodoItemCreator コンポーネントは todo を新規追加するコンポーネントです。TodoListState の内容を更新するセッター関数にアクセスします。
function TodoItemCreator() {
const [inputValue, setInputValue] = useState("");
const setTodoList = useSetRecoilState(todoListState);
const addItem = () => {
setTodoList((oldTodoList) => [
...oldTodoList,
{
id: getId(),
text: inputValue,
isComplete: false
}
]);
setInputValue("");
};
const onChange = ({ target: { value } }) => {
setInputValue(value);
};
return (
<div>
<input type="text" value={inputValue} onChange={onChange} />
<button onClick={addItem}>Add</button>
</div>
);
}
export default TodoItemCreator;
let id = 0;
function getId() {
return id++;
}
TodoItem コンポーネントは、Todo 項目の値を表示すると同時に、そのテキストを変更したり、項目を削除したりすることができます。useRecoilState() で todoListState を読み込み、セッター関数を取得して、項目のテキストを更新したり、完了マークをつけたり、削除するのに使っています。
function TodoItem({ item }) {
const [todoList, setTodoList] = useRecoilState(todoListState);
const index = todoList.findIndex((listItem) => listItem === item);
const editItemText = ({ target: { value } }) => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
text: value
});
setTodoList(newList);
};
const toggleItemCompletion = () => {
const newList = replaceItemAtIndex(todoList, index, {
...item,
isComplete: !item.isComplete
});
setTodoList(newList);
};
const deleteItem = () => {
const newList = removeItemIndex(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>
);
}
export default TodoItem;
function replaceItemAtIndex(arr, index, newValue) {
return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
}
function removeItemIndex(arr, index) {
return [...arr.slice(0, index), ...arr.slice(index + 1)];
}