完了、未完了のロジックがうまくいかない
Q&A
Closed
解決したいこと
Recoilで状態管理をして、todo管理の簡易アプリを作成しています。TodoItem.tsx中のボタンをクリックした時に「完了」、「未完了」を切り替える実装を行いたく、出ているエラーに対して助言を頂きたいです。
発生している問題・エラー
Uncaught TypeError: Cannot assign to read only property 'isComplete' of object '#<Object>'
、コンソールにエラーとしてはかれる。
TodoItem.tsx
const toggleCompletion = (id: number, isComplete: boolean) => {
const toggleChecked = todoList.map((todo) => {
if (todo.id === id) {
todo.isComplete = !isComplete;
}
return todo;
});
setTodoList(toggleChecked);
};
上記のtodo.isComplete = !isComplete
の部分で出現する。
該当するソースコード
TodoList.tsx
import React from 'react';
import { useRecoilValue } from 'recoil';
import { todoListState } from '../State/atom';
import TodoItem from './TodoItem';
import TodoItemCreator from './TodoItemCreator';
import TodoListStatus from './TodoListStatus';
const TodoList = () => {
const todoList = useRecoilValue(todoListState);
return (
<div>
<h1>初めてのRecoil</h1>
<TodoListStatus />
<TodoItemCreator />
{todoList.map((todo) => (
<TodoItem key={todo.id} todo={todo} />
))}
</div>
);
};
export default TodoList;
TodoItem.tsx
import React from 'react';
import { useRecoilState } from 'recoil';
import { todoListState } from '../State/atom';
import { ITEMPROPS } from '../types/DataType';
const TodoItem: React.FC<ITEMPROPS> = ({ todo }) => {
const [todoList, setTodoList] = useRecoilState(todoListState);
const deleteItem = (id: number) => {
const newTodos = todoList.filter((todo) => todo.id !== id);
setTodoList(newTodos);
};
const toggleCompletion = (id: number, isComplete: boolean) => {
const toggleChecked = todoList.map((todo) => {
if (todo.id === id) {
todo.isComplete = !isComplete;
}
return todo;
});
setTodoList(toggleChecked);
};
return (
<div>
<div key={todo.id}>
<button onClick={() => toggleCompletion(todo.id, todo.isComplete)}>
{todo.isComplete ? '完了' : '未完了'}
</button>
{todo.title}
<span className='deletefunc' onClick={() => deleteItem(todo.id)}>
削除
</span>
</div>
</div>
);
};
export default TodoItem;
atom.ts
//ストア的な
import { atom } from 'recoil';
import { DATA } from '../types/DataType';
export const todoListState = atom<Array<DATA>>({
key: 'todoListState',
default: [
{
id: 0,
title: '送信設定',
isComplete: false,
},
],
});
selector.ts
//参照
import { selector } from 'recoil';
import { todoListState } from './atom';
export const todoListStatusState = selector<number>({
key: 'todoListStatusState',
get: ({ get }) => {
const todoList = get(todoListState);
const totalNum = todoList.length;
return totalNum;
},
});
DataType.ts
//型定義
export type DATA = {
id: number;
title: string;
isComplete: boolean;
};
export type ITEMPROPS = {
todo: DATA;
};
自分で試したこと
クリックされるたびに下記の関数(toggleCompletion)が実行されて切り替わるという想定をしました。
idが一致した時に完了と未完了の表示を切り替えるというのを考えていましたが、うまく実装できませんでした。
TodoItem.tsx
const toggleCompletion = (id: number, isComplete: boolean) => {
const toggleChecked = todoList.map((todo) => {
if (todo.id === id) {
todo.isComplete = !isComplete;
}
return todo;
});
setTodoList(toggleChecked);
};
0