Skip to content
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
REACT_APP_API_ENDPOINT=https://dev.jsonplaceholder.typicode.com/todos
REACT_APP_API_ENDPOINT=https://jsonplaceholder.typicode.com/todos
16 changes: 14 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,25 @@ import TaskList from './components/TaskList';
import { useFetch } from './hooks/useFetch';

const App = () => {
const [state, tasks] = useFetch(process.env.REACT_APP_API_ENDPOINT);
const [state, tasks, update, add] = useFetch(
Comment thread
iakomus marked this conversation as resolved.
process.env.REACT_APP_API_ENDPOINT,
);

const updateTask = (task) => {
update(task.id, task);
};

const addTask = (title) => {
add({ title, completed: false });
};

return (
<>
<h1>TODO List</h1>
{state === 'PENDING' && <Loading />}
{state === 'SUCCESS' && <TaskList tasks={tasks} />}
{state === 'SUCCESS' && (
<TaskList tasks={tasks} updateTask={updateTask} addTask={addTask} />
)}
{state === 'FAILED' && <Error>Something went wrong</Error>}
</>
);
Expand Down
24 changes: 7 additions & 17 deletions src/components/Task.js
Original file line number Diff line number Diff line change
@@ -1,36 +1,26 @@
import { useState } from 'react';

const Task = ({ task }) => {
const [item, setItem] = useState(task);

const inputId = `task-${item.id}`;
const Task = ({ task, updateTask }) => {
const inputId = `task-${task.id}`;

const handleInputChange = (event) => {
setItem({
...item,
updateTask({
...task,
completed: event.target.checked,
});

fetch(`${process.env.REACT_APP_API_ENDPOINT}/${item.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ completed: event.target.checked }),
});
};

return (
<li>
<input
id={inputId}
type="checkbox"
checked={item.completed}
checked={task.completed}
onChange={handleInputChange}
/>
<label
style={item.completed ? { textDecoration: 'line-through' } : {}}
style={task.completed ? { textDecoration: 'line-through' } : {}}
htmlFor={inputId}
>
{item.title}
{task.title}
</label>
</li>
);
Expand Down
28 changes: 28 additions & 0 deletions src/components/TaskForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { useInput } from '../hooks/useInput';

const TaskForm = ({ addTask }) => {
const [{ task }, update, reset] = useInput({ task: '' });

const handleInputChange = (event) => {
update(event);
};

const handleSubmit = (event) => {
event.preventDefault();
addTask(task);
reset();
};

return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="task"
value={task}
onChange={handleInputChange}
/>
</form>
);
};

export default TaskForm;
8 changes: 6 additions & 2 deletions src/components/TaskList.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import Task from './Task';
import TaskForm from './TaskForm';

const TaskList = ({ tasks }) => {
const TaskList = ({ tasks, updateTask, addTask }) => {
return (
<ul>
{tasks.map((task) => (
<Task key={task.id} task={task} />
<Task key={task.id} task={task} updateTask={updateTask} />
))}
<li>
<TaskForm addTask={addTask} />
</li>
</ul>
);
};
Expand Down
31 changes: 30 additions & 1 deletion src/hooks/useFetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export const useFetch = (url) => {
const [requestState, setRequestState] = useState('INITIAL');

useEffect(() => {
if (!url) {
return;
}

setRequestState('PENDING');

fetch(url)
Expand All @@ -16,5 +20,30 @@ export const useFetch = (url) => {
.catch(() => setRequestState('FAILED'));
}, [url]);

return [requestState, requestResponse];
const patchRequest = async (id, request) => {
await fetch(`${process.env.REACT_APP_API_ENDPOINT}/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});

setRequestResponse((previous) => {
return previous.map((object) => (object.id === id ? request : object));
});
};

const postRequest = async (request) => {
const response = await fetch(`${process.env.REACT_APP_API_ENDPOINT}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(request),
});
const json = await response.json();

setRequestResponse((previous) => {
return [...previous, json];
});
};

return [requestState, requestResponse, patchRequest, postRequest];
};
17 changes: 17 additions & 0 deletions src/hooks/useInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useState } from 'react';

export const useInput = (initialValues) => {
const [input, setInput] = useState(initialValues);

const update = (event) => {
setInput((previous) => {
return { ...previous, [event.target.name]: event.target.value };
});
};

const reset = () => {
setInput(initialValues);
};

return [input, update, reset];
};