I'm working on a Todo App, I'd like to replace a particular div element (when a button is clicked) with a form component.
There’s this particular component that renders all my todos. on each todo item it renders, there’s a description and two buttons. one of the buttons is for deleting the particular todo item, the other is for updating it. I don’t have an issue deleting a particular todo. where i have an issue is updating a particular one. When I click the update button, it’s supposed to replace that particular todo item with a custom form component. then I can click update on that form and it’ll update, in theory. Tthe actual problem now is that when I click the update button on a particular todo item, it replaces all the todo items w the custom form component.
const Todos: React.FC<Props> = props => {
const ctx = useContext(TodoContext);
const updateForm = <TodoUpdateForm />;
const editTodo = (value: Todo) => {
ctx?.setClicked(true);
const updatedTodo = ctx?.todos.filter(todo => todo.id === value.id);
console.log(updatedTodo);
};
const completeTodo = (todo: string) => ctx?.completeTodo(todo);
const removeTodo = (todo: string) => ctx?.removeTodo(todo);
return (
<div>
<AnimatePresence>
{ctx?.todos.map((todo, index) => {
return (
<motion.div
key={index}
whileInView={{ x: [-100, 0], opacity: [0, 1] }}
exit={{ x: [0, 20], opacity: [1, 0] }}
transition={{
duration: 1,
ease: 'easeInOut',
delayChildren: 0.5,
}}
className={`${
todo.isComplete ? 'todo-row complete' : 'todo-row'
} app__flex`}
>
{ctx.isClicked ? (
updateForm
) : (
<div key={todo.id} className="todos-item">
<div
key={todo.id}
onClick={() => completeTodo(todo.id)}
className="app__todo-list-item"
>
{todo.text}
</div>
<motion.div
whileInView={{ opacity: [0, 1] }}
transition={{
duration: 2,
ease: 'easeInOut',
delayChildren: 0.5,
}}
className="icons app__flex"
>
<RiCloseCircleLine
className="delete-icon"
onClick={() => removeTodo(todo.id)}
/>
<TiEdit
className="edit-icon"
onClick={() => editTodo(todo)}
/>
</motion.div>
</div>
)}
</motion.div>
);
})}
</AnimatePresence>
</div>
);
};
export default Todos;
the Context Provider
export const TodoContext = React.createContext<TodoContextObj | null>({
todos: [],
edit: { id: '', value: '' },
isClicked: false,
addTodo: (todo: Todo) => {},
removeTodo: (id: string) => {},
completeTodo: (id: string) => {},
updateTodo: (id: string, todo: Todo) => {},
deleteTodos: (key: string) => {},
setClicked: (state: boolean) => {},
});
type Props = { children: React.ReactNode };
const initialValue: InitialState = { id: null, value: '' };
const ContextProvider: React.FC<Props> = props => {
const [todos, setTodos] = useState<Array<Todo>>([]);
const [edit, setEdit] = useState(initialValue);
const [isClicked, setIsClicked] = useState(false);
useEffect(() => {
const data = localStorage.getItem('todos');
if (data) setTodos(JSON.parse(data));
return () => {};
}, []);
const setClicked = (state: boolean) => setIsClicked(state);
const addTodo = (todo: Todo) => {
// Guard Clause
if (!todo.text || /^\s*$/.test(todo.text)) return;
const newTodos = [...todos, todo];
setTodos(newTodos);
setIsClicked(false);
localStorage.setItem('todos', JSON.stringify(newTodos));
};
const updateTodo = (id: string, value: Todo) => {
// Guard Clause
if (!value.text || /^\s*$/.test(value.text)) return;
if (edit.id) setEdit({ id: value.id, value: value.text });
localStorage.removeItem('todos');
setTodos(prev => prev.map(item => (item.id === id ? value : item)));
setIsClicked(false);
localStorage.setItem('todos', JSON.stringify(todos));
};
const removeTodo = (id: string) => {
const updatedTodo = [...todos].filter(todo => todo.id !== id);
localStorage.removeItem('todos');
setTodos(updatedTodo);
localStorage.setItem('todos', JSON.stringify(updatedTodo));
};
const completeTodo = (id: string) => {
let updateTodos = todos.map(todo => {
if (todo.id === id) todo.isComplete = !todo.isComplete;
return todo;
});
setTodos(updateTodos);
};
const deleteTodos = (key: string) => {
localStorage.removeItem(key);
setTodos([]);
};
const contextValue: TodoContextObj = {
todos,
isClicked,
addTodo,
removeTodo,
completeTodo,
updateTodo,
deleteTodos,
setClicked,
};
return (
<TodoContext.Provider value={contextValue}>
{props.children}
</TodoContext.Provider>
);
};
export default ContextProvider;