├── .gitignore
├── README.md
├── db.json
├── index.html
├── package-lock.json
├── package.json
├── public
└── vite.svg
├── src
├── App.jsx
├── api
│ └── posts.jsx
├── components
│ ├── AddPost.jsx
│ └── PostForm.jsx
├── main.jsx
└── pages
│ ├── EditPost.jsx
│ ├── Post.jsx
│ └── PostLists.jsx
└── vite.config.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [vite](https://vitejs.dev/guide/#scaffolding-your-first-vite-project).
2 |
3 |
4 | ## Available Scripts
5 |
6 | In the project directory, you can run:
7 |
8 | ### `npm install`
9 |
10 | Instal All dependencies in this project
11 |
12 | ### `npm run dev`
13 |
14 | Runs the app in the development mode.
15 | Open [http://127.0.0.1:5173](http://127.0.0.1:5173) to view it in the browser.
16 |
17 | ### Link
18 |
19 | - Tanstack react-query: https://tanstack.com/query
20 | - vite: https://vitejs.dev
21 | - json-server: https://github.com/typicode/json-server
22 | - uuid: https://github.com/uuidjs/uuid
23 |
24 | ### Video Tutorial
25 |
26 | You can see my youtube video for this project in [here](https://youtu.be/AAMBoENvfnE)
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "posts": []
3 | }
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "crud-react-query",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "@tanstack/react-query": "^4.24.10",
13 | "@tanstack/react-query-devtools": "^4.24.12",
14 | "json-server": "^0.17.2",
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "react-router-dom": "^6.8.2",
18 | "uuid": "^9.0.0"
19 | },
20 | "devDependencies": {
21 | "@types/react": "^18.0.27",
22 | "@types/react-dom": "^18.0.10",
23 | "@vitejs/plugin-react": "^3.1.0",
24 | "vite": "^4.1.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { Route, Routes } from "react-router-dom"
2 | import EditPost from "./pages/EditPost"
3 | import Post from "./pages/Post"
4 | import PostLists from "./pages/PostLists"
5 |
6 | function App() {
7 |
8 | return (
9 |
10 |
Awesome blog
11 |
12 | } />
13 | } />
14 | } />
15 |
16 |
17 | )
18 | }
19 |
20 | export default App
21 |
--------------------------------------------------------------------------------
/src/api/posts.jsx:
--------------------------------------------------------------------------------
1 | export async function fetchPosts() {
2 | const response = await fetch('http://localhost:3000/posts');
3 | return response.json()
4 | }
5 |
6 | export async function fetchPost(id) {
7 | const response = await fetch(`http://localhost:3000/posts/${id}`);
8 | return response.json()
9 | }
10 |
11 | export async function createPost(newPost) {
12 | const response = await fetch(`http://localhost:3000/posts`, {
13 | method: "POST",
14 | headers: {
15 | "Content-Type": "application/json"
16 | },
17 | body: JSON.stringify(newPost)
18 | });
19 | return response.json()
20 | }
21 |
22 | export async function updatePost(updatedPost) {
23 | const response = await fetch(`http://localhost:3000/posts/${updatedPost.id}`, {
24 | method: "PUT",
25 | headers: {
26 | "Content-Type": "application/json"
27 | },
28 | body: JSON.stringify(updatedPost)
29 | });
30 | return response.json()
31 | }
32 |
33 | export async function deletePost(id) {
34 | const response = await fetch(`http://localhost:3000/posts/${id}`, {
35 | method: "DELETE",
36 | });
37 | return response.json()
38 | }
--------------------------------------------------------------------------------
/src/components/AddPost.jsx:
--------------------------------------------------------------------------------
1 | import { useMutation, useQueryClient } from "@tanstack/react-query"
2 | import { createPost } from "../api/posts"
3 | import PostForm from "./PostForm"
4 | import { v4 as uuidv4 } from 'uuid';
5 |
6 | const AddPost = () => {
7 | const queryClient = useQueryClient();
8 |
9 | const createPostMutation = useMutation({
10 | mutationFn: createPost,
11 | onSuccess: () => {
12 | queryClient.invalidateQueries({ queryKey: ['posts']});
13 | console.log("success bro!")
14 | }
15 | });
16 |
17 | const handleAddPost = (post) => {
18 | createPostMutation.mutate({
19 | id: uuidv4(),
20 | ...post
21 | })
22 | }
23 |
24 | return (
25 |
26 |
Add new post
27 |
28 |
29 | )
30 | }
31 |
32 | export default AddPost
--------------------------------------------------------------------------------
/src/components/PostForm.jsx:
--------------------------------------------------------------------------------
1 | import { useState } from "react"
2 |
3 | const PostForm = ({ onSubmit, initialValue }) => {
4 | const [post, setPost] = useState({
5 | title: initialValue.title || "",
6 | body: initialValue.body || ""
7 | });
8 |
9 | const handleChangeInput = (e) => {
10 | setPost({
11 | ...post,
12 | [e.target.name]: e.target.value
13 | })
14 | }
15 |
16 | const renderField = (label) => (
17 |
18 |
19 |
20 |
21 | );
22 |
23 | const handleSubmit = (e) => {
24 | e.preventDefault();
25 | onSubmit(post);
26 | setPost({
27 | title: "",
28 | body: ""
29 | })
30 |
31 | }
32 |
33 | return (
34 |
39 | )
40 | }
41 |
42 | export default PostForm
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2 | import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
3 | import React from "react";
4 | import ReactDOM from "react-dom/client";
5 | import { BrowserRouter } from "react-router-dom";
6 | import App from "./App";
7 |
8 | // create a client
9 | const queryClient = new QueryClient();
10 |
11 | ReactDOM.createRoot(document.getElementById("root")).render(
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/src/pages/EditPost.jsx:
--------------------------------------------------------------------------------
1 | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2 | import { useNavigate, useParams } from "react-router-dom";
3 | import { fetchPost, updatePost } from "../api/posts";
4 | import PostForm from "../components/PostForm"
5 |
6 | const EditPost = () => {
7 | const queryClient = useQueryClient();
8 | const navigate = useNavigate();
9 | const { id } = useParams();
10 | const {
11 | isLoading,
12 | isError,
13 | data: post,
14 | error,
15 | } = useQuery({
16 | queryKey: ["posts", id],
17 | queryFn: () => fetchPost(id),
18 | });
19 | const updatePostMutation = useMutation({
20 | mutationFn: updatePost,
21 | onSuccess: () => {
22 | queryClient.invalidateQueries({ queryKey: ['posts']});
23 | navigate("/")
24 | }
25 | })
26 |
27 | if (isLoading) return "loading...";
28 | if (isError) return `Error: ${error.message}`;
29 |
30 | const handleSubmit = (updatedPost) => {
31 | updatePostMutation.mutate({id, ...updatedPost})
32 | }
33 |
34 |
35 | return (
36 |
39 | )
40 | }
41 |
42 | export default EditPost
--------------------------------------------------------------------------------
/src/pages/Post.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { useNavigate, useParams } from "react-router-dom";
3 | import { fetchPost } from "../api/posts";
4 |
5 | const Post = () => {
6 | const navigate = useNavigate();
7 | const { id } = useParams();
8 | const {
9 | isLoading,
10 | isError,
11 | data: post,
12 | error,
13 | } = useQuery({
14 | queryKey: ["posts", id],
15 | queryFn: () => fetchPost(id),
16 | });
17 |
18 | if (isLoading) return "loading...";
19 | if (isError) return `Error: ${error.message}`;
20 |
21 |
22 | return (
23 |
24 |
25 |
{post.title}
26 |
{post.body}
27 |
28 | )
29 | }
30 |
31 | export default Post
--------------------------------------------------------------------------------
/src/pages/PostLists.jsx:
--------------------------------------------------------------------------------
1 | import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
2 | import { useNavigate } from "react-router-dom";
3 | import { deletePost, fetchPosts } from "../api/posts";
4 | import AddPost from "../components/AddPost";
5 |
6 | const PostLists = () => {
7 | const navigate = useNavigate();
8 | const queryClient = useQueryClient();
9 |
10 | const {
11 | isLoading,
12 | isError,
13 | data: posts,
14 | error,
15 | } = useQuery({
16 | queryKey: ["posts"],
17 | queryFn: fetchPosts,
18 | });
19 |
20 | const deletePostMutation = useMutation({
21 | mutationFn: deletePost,
22 | onSuccess: () => {
23 | queryClient.invalidateQueries({ queryKey: ['posts']});
24 | }
25 | });
26 |
27 | const handleDelete = (id) => {
28 | deletePostMutation.mutate(id)
29 | }
30 |
31 | if (isLoading) return "loading...";
32 | if (isError) return `Error: ${error.message}`;
33 |
34 | return (
35 |
36 |
37 | {posts.map((post) => (
38 |
39 |
navigate(`/post/${post.id}`)}
42 | >
43 | {post.title}
44 |
45 |
46 |
47 |
48 | ))}
49 |
50 | );
51 | };
52 |
53 | export default PostLists;
54 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------