diff --git a/.env b/.env index 5bb4b74..50bf507 100644 --- a/.env +++ b/.env @@ -1 +1 @@ -REACT_APP_API_ENDPOINT=https://dev.jsonplaceholder.typicode.com/todos +REACT_APP_API_ENDPOINT=https://jsonplaceholder.typicode.com/todos diff --git a/src/App.js b/src/App.js index 8f4e8de..3940c2d 100644 --- a/src/App.js +++ b/src/App.js @@ -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( + process.env.REACT_APP_API_ENDPOINT, + ); + + const updateTask = (task) => { + update(task.id, task); + }; + + const addTask = (title) => { + add({ title, completed: false }); + }; return ( <>

TODO List

{state === 'PENDING' && } - {state === 'SUCCESS' && } + {state === 'SUCCESS' && ( + + )} {state === 'FAILED' && Something went wrong} ); diff --git a/src/components/Task.js b/src/components/Task.js index e205dae..af8c84d 100644 --- a/src/components/Task.js +++ b/src/components/Task.js @@ -1,21 +1,11 @@ -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 ( @@ -23,14 +13,14 @@ const Task = ({ task }) => { ); diff --git a/src/components/TaskForm.js b/src/components/TaskForm.js new file mode 100644 index 0000000..dbca0a2 --- /dev/null +++ b/src/components/TaskForm.js @@ -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 ( +
+ +
+ ); +}; + +export default TaskForm; diff --git a/src/components/TaskList.js b/src/components/TaskList.js index 613a3d7..2e63cef 100644 --- a/src/components/TaskList.js +++ b/src/components/TaskList.js @@ -1,11 +1,15 @@ import Task from './Task'; +import TaskForm from './TaskForm'; -const TaskList = ({ tasks }) => { +const TaskList = ({ tasks, updateTask, addTask }) => { return (
    {tasks.map((task) => ( - + ))} +
  • + +
); }; diff --git a/src/hooks/useFetch.js b/src/hooks/useFetch.js index ccc34dc..06010f0 100644 --- a/src/hooks/useFetch.js +++ b/src/hooks/useFetch.js @@ -5,6 +5,10 @@ export const useFetch = (url) => { const [requestState, setRequestState] = useState('INITIAL'); useEffect(() => { + if (!url) { + return; + } + setRequestState('PENDING'); fetch(url) @@ -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]; }; diff --git a/src/hooks/useInput.js b/src/hooks/useInput.js new file mode 100644 index 0000000..310af22 --- /dev/null +++ b/src/hooks/useInput.js @@ -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]; +};