티스토리 뷰
728x90
반응형
import { atom, selector } from 'recoil';
import axios from 'axios';
export interface Todo {
id: number;
title: string;
completed: boolean;
}
// Atom for managing todos
export const todosState = atom<Todo[]>({
key: 'todosState',
default: [],
});
// Selector to fetch todos from server
export const todosSelector = selector<Todo[]>({
key: 'todosSelector',
get: async () => {
try {
const response = await axios.get('https://jsonplaceholder.typicode.com/todos');
return response.data.slice(0, 10); // Limiting to 10 items for simplicity
} catch (error) {
console.error('Failed to fetch todos:', error);
return [];
}
},
});
import React, { useState, useEffect } from 'react';
import { useRecoilState, useRecoilValueLoadable } from 'recoil';
import { todosState, todosSelector, Todo } from './recoilState';
import axios from 'axios';
function Todos() {
const [todos, setTodos] = useRecoilState(todosState);
const [newTitle, setNewTitle] = useState('');
const [editingId, setEditingId] = useState<number | null>(null);
const [editTitle, setEditTitle] = useState('');
const [error, setError] = useState<string | null>(null);
const todosLoadable = useRecoilValueLoadable(todosSelector);
const API_BASE_URL = 'https://jsonplaceholder.typicode.com/todos';
// Fetch todos from the server on initial load
useEffect(() => {
if (todosLoadable.state === 'hasValue' && todosLoadable.contents.length > 0) {
setTodos(todosLoadable.contents);
}
}, [todosLoadable]);
// Optimistic Add
const handleAdd = async () => {
if (newTitle.trim() === '') return;
const newTodo: Todo = {
id: Date.now(),
title: newTitle,
completed: false,
};
setTodos((prev) => [...prev, newTodo]); // Optimistic UI
setNewTitle('');
try {
const response = await axios.post(API_BASE_URL, newTodo);
setTodos((prev) =>
prev.map((todo) => (todo.id === newTodo.id ? response.data : todo))
);
} catch (error) {
console.error('Failed to create todo:', error);
setError('Failed to add the todo. Try again.');
setTodos((prev) => prev.filter((todo) => todo.id !== newTodo.id)); // Rollback on error
}
};
// Optimistic Update
const handleUpdate = async (id: number) => {
const originalTodo = todos.find((todo) => todo.id === id);
if (!originalTodo) return;
setTodos((prev) =>
prev.map((todo) => (todo.id === id ? { ...todo, title: editTitle } : todo))
);
try {
const response = await axios.patch(`${API_BASE_URL}/${id}`, { title: editTitle });
setTodos((prev) =>
prev.map((todo) => (todo.id === id ? response.data : todo))
);
} catch (error) {
console.error('Failed to update todo:', error);
setError('Failed to update the todo. Try again.');
setTodos((prev) => [...prev, originalTodo]); // Rollback on error
} finally {
setEditingId(null);
setEditTitle('');
}
};
// Optimistic Delete
const handleDelete = async (id: number) => {
const originalTodos = [...todos];
setTodos((prev) => prev.filter((todo) => todo.id !== id)); // Optimistic UI
try {
await axios.delete(`${API_BASE_URL}/${id}`);
} catch (error) {
console.error('Failed to delete todo:', error);
setError('Failed to delete the todo. Try again.');
setTodos(originalTodos); // Rollback on error
}
};
// Auto-sync with server every 10 seconds
useEffect(() => {
const interval = setInterval(async () => {
try {
const response = await axios.get(API_BASE_URL);
setTodos(response.data.slice(0, 10)); // Update state with latest data
} catch (error) {
console.error('Failed to sync todos:', error);
}
}, 10000);
return () => clearInterval(interval); // Cleanup on unmount
}, []);
return (
<div>
<h2>Todos</h2>
{error && <p style={{ color: 'red' }}>{error}</p>}
<ul>
{todos.map((todo) => (
<li key={todo.id}>
{editingId === todo.id ? (
<input
type="text"
value={editTitle}
onChange={(e) => setEditTitle(e.target.value)}
/>
) : (
<span>{todo.title}</span>
)}
<button onClick={() => handleDelete(todo.id)}>Delete</button>
{editingId === todo.id ? (
<button onClick={() => handleUpdate(todo.id)}>Save</button>
) : (
<button onClick={() => setEditingId(todo.id)}>Edit</button>
)}
</li>
))}
</ul>
<input
type="text"
value={newTitle}
onChange={(e) => setNewTitle(e.target.value)}
placeholder="Add a new todo"
/>
<button onClick={handleAdd}>Add</button>
</div>
);
}
export default Todos;
728x90
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
TAG
- jstl(java standard tag library)
- system.io
- nl2br
- 람다식(lambda expression)
- 특정 문자를 기준으로 자르기
- 인텔리제이(intellij)
- jsp 오픈 소스
- 스프링 시큐리티(spring security)
- docker
- MainActor
- System.Diagnostics
- 진수 변환
- 표현 언어(expression language)
- java web-mvc
- await
- In App Purchase
- 스프링 시큐리티(spring security)-http basic 인증
- java-개발 환경 설정하기
- React
- error-java
- REST API
- 스프링 프레임워크(spring framework)
- .submit()
- 스프링 프레임워크(spring framewordk)
- 문자 자르기
- java.sql
- 제품 등록
- jstl(java standard tag library)-core
- 메이븐(maven)
- java 키워드 정리
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
글 보관함