Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 2 additions & 24 deletions tutorial-react-ts/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,11 @@
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import BlogApp from './blog/blogApp'

function App() {
const [count, setCount] = useState(0)

return (
<>
<div>
<a href="https://vite.dev" target="_blank">
<img src={viteLogo} className="logo" alt="Vite logo" />
</a>
<a href="https://react.dev" target="_blank">
<img src={reactLogo} className="logo react" alt="React logo" />
</a>
</div>
<h1>Vite + React</h1>
<div className="card">
<button onClick={() => setCount((count) => count + 1)}>
count is {count}
</button>
<p>
Edit <code>src/App.tsx</code> and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
<BlogApp />
</>
)
}
Expand Down
24 changes: 24 additions & 0 deletions tutorial-react-ts/src/blog/blogApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { PostType } from "./definitions/PostType";
import PostItem from "./ui/PostItem";
import useHandlePost from "./hooks/useHandlePost";

function BlogApp() {

const { posts, handleUpdate, handleDelete, handleAddPost } = useHandlePost();

return (
<div style={{ width: 600 }}>
{posts.map((post: PostType) => (
<PostItem
key={post.id}
post={post}
handleUpdate={handleUpdate}
handleDelete={handleDelete} />
))}
<button onClick={handleAddPost}>Add Post</button>
</div>);
}

export default BlogApp


8 changes: 8 additions & 0 deletions tutorial-react-ts/src/blog/definitions/PostActionType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PostType } from "./PostType";



export type PostActionType = { type: "SET_POSTS"; payload: PostType[]; } |
{ type: "ADD_POST"; payload: PostType; } |
{ type: "UPDATE_POST"; payload: { id: number; updatedFields: Partial<PostType>; }; } |
{ type: "DELETE_POST"; payload: number; };
8 changes: 8 additions & 0 deletions tutorial-react-ts/src/blog/definitions/PostItemProps.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PostType } from "./PostType";


export type PostItemProps = {
post: PostType;
handleUpdate: (id: number) => void;
handleDelete: (id: number) => void;
};
8 changes: 8 additions & 0 deletions tutorial-react-ts/src/blog/definitions/PostType.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

export type PostType = {
userId: number;
id: number;
title: string;
body: string;
};

29 changes: 29 additions & 0 deletions tutorial-react-ts/src/blog/hooks/useHandlePost.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import usePostData from "./usePostData";

function useHandlePost() {

const { posts,postFunctions } = usePostData()

const handleAddPost = () => {
const newPost = {
title: 'Titolo',
userId: 1,
body: 'Paragrafo inserito'
};
postFunctions.AddPost(newPost);
};

const handleUpdate = (id: number) => {
postFunctions.UpdatePost(id, {
title: 'Nuovo Titolo',
body: 'Contenuto aggiornato'
});
};

const handleDelete = (id: number) => {
postFunctions.DeletePost(id);
};
return { posts,handleUpdate, handleDelete, handleAddPost };
}

export default useHandlePost
92 changes: 92 additions & 0 deletions tutorial-react-ts/src/blog/hooks/usePostData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { useEffect, useReducer } from "react";
import {postReducer} from "../reducer/postReducer";
import { PostType } from "../definitions/PostType";

function usePostData() {
const [posts,dispatch] = useReducer(postReducer,[])

useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) => response.json())
.then((data) => dispatch({type:'SET_POSTS', payload:data}));
}, []);

const postFunctions = {
AddPost: (newPost: Omit<PostType, 'id'>) => {
fetch("https://jsonplaceholder.typicode.com/posts", {
method: "POST",
body: JSON.stringify({
title: newPost.title,
body: newPost.body,
userId: newPost.userId
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
.then((response) => {
if (!response.ok) throw new Error("Errore nell'invio del post");
return response.json();
})
.then((json) => {
dispatch({type:'ADD_POST', payload:json})
console.log("Post aggiunto:", json);
})
.catch((error) => console.error("Errore:", error));
},
UpdatePost: (id: number, updateFields: Partial<PostType>) => {
const postUpdate = posts.find((post:PostType) => post.id === id)

if (!postUpdate) {
console.error(`Post non trovato per l'id:`, id);
return
}


fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: "PUT",
body: JSON.stringify({
...postUpdate,
...updateFields,
}),
headers: {
"Content-type": "application/json; charset=UTF-8",
},
})
.then((response) => {
if (!response.ok) throw new Error("Errore nell'aggiornamento del post");
return response.json();
})
.then((json) => {
dispatch({type:'UPDATE_POST', payload:{id,updatedFields:json}})
window.confirm("Post aggiornato");
})
.catch((error) => console.error("Errore:", error));
},
DeletePost: (id: number) => {
if (!window.confirm("Sei sicuro di voler eliminare questo post?")) return;

fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: "DELETE",
})
.then((response) => {
if (!response.ok) throw new Error("Errore nell'eliminazione del post");
window.confirm(`Post con ID ${id} eliminato con successo.`);
dispatch({type:'DELETE_POST', payload:id})
})
.catch((error) =>
console.error("Errore durante l'eliminazione del post:", error)
);
}
}


return {
posts,
postFunctions
}
}
export default usePostData



23 changes: 23 additions & 0 deletions tutorial-react-ts/src/blog/reducer/postReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { PostActionType } from "../definitions/PostActionType";
import { PostType } from "../definitions/PostType";


export function postReducer(state: PostType[], action: PostActionType) {
switch (action.type) {
case 'SET_POSTS':
return action.payload
case 'ADD_POST':
return [...state, action.payload]

case "UPDATE_POST":
return state.map((post) =>
post.id === action.payload.id ?
{ ...post, ...action.payload.updatedFields }
: post
)
case "DELETE_POST":
return state.filter((post) => post.id !== action.payload)
default:
return state;
}
}
16 changes: 16 additions & 0 deletions tutorial-react-ts/src/blog/ui/PostItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { PostItemProps } from "../definitions/PostItemProps";

export function PostItem(props: PostItemProps) {

const {post, handleUpdate, handleDelete} = props;

return (
<div key={post.id} id={post.id.toString()}>
<h2>{post.title}</h2>
<div>{post.body}</div>
<button onClick={() => handleUpdate(post.id)}>Update Post</button>
<button onClick={() => handleDelete(post.id)}>Delete Post</button>
</div>
);
}
export default PostItem;