├── .gitignore ├── README.md ├── db.json ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── api └── posts.js ├── components ├── NewPost.jsx ├── Post.jsx └── Posts.jsx ├── hooks └── posts.js └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React-Query Tutorial | Consultar APIs, caché y sincronización de forma simple y eficiente 🚀 2 | 3 | ## Tutoriales 4 | ### Parte 1: Consultas 5 | [![React-Query Tutorial | Consultar APIs, caché y sincronización de forma simple y eficiente 🚀](https://img.youtube.com/vi/lNkAJCoXg6I/0.jpg)](https://www.youtube.com/watch?v=lNkAJCoXg6I "React-Query Tutorial | Consultar APIs, caché y sincronización de forma simple y eficiente 🚀") 6 | 7 | ### Parte 2: Mutaciones 8 | [![React-Query Mutations Tutorial | Actualizar datos del servidor de forma simple y eficiente 🚀](https://img.youtube.com/vi/X1qo8qEaWLg/0.jpg)](https://www.youtube.com/watch?v=X1qo8qEaWLg "React-Query Mutations Tutorial | Actualizar datos del servidor de forma simple y eficiente 🚀") 9 | 10 | ## Para iniciar la aplicación en modo desarrollo, usar los siguientes scripts 11 | 12 | ### `npm install` 13 | Instala las dependencias 14 | ### `npm start` 15 | Inicia la aplicación en modo desarrollo en el puerto `3000` 16 | ### `npm run server` 17 | Inicia el servidor de `json-server` en el puerto `3005` para probar el ejemplo 18 | -------------------------------------------------------------------------------- /db.json: -------------------------------------------------------------------------------- 1 | { 2 | "posts": [ 3 | { 4 | "id": 1, 5 | "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", 6 | "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" 7 | }, 8 | { 9 | "id": 2, 10 | "title": "qui est esse", 11 | "body": "est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla" 12 | }, 13 | { 14 | "id": 3, 15 | "title": "ea molestias quasi exercitationem repellat qui ipsa sit aut", 16 | "body": "et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut" 17 | }, 18 | { 19 | "id": 4, 20 | "title": "eum et est occaecati", 21 | "body": "ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit" 22 | }, 23 | { 24 | "id": 5, 25 | "title": "nesciunt quas odio", 26 | "body": "repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque" 27 | }, 28 | { 29 | "id": 6, 30 | "title": "dolorem eum magni eos aperiam quia", 31 | "body": "ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae" 32 | }, 33 | { 34 | "id": 7, 35 | "title": "magnam facilis autem", 36 | "body": "dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas" 37 | }, 38 | { 39 | "title": "aaa", 40 | "body": "aaa", 41 | "id": 8 42 | }, 43 | { 44 | "title": "new post", 45 | "body": "aaa", 46 | "id": 9 47 | }, 48 | { 49 | "title": "a", 50 | "body": "a", 51 | "id": 10 52 | }, 53 | { 54 | "title": "a", 55 | "body": "a", 56 | "id": 11 57 | }, 58 | { 59 | "title": "adf", 60 | "body": "asdf", 61 | "id": 12 62 | }, 63 | { 64 | "title": "a", 65 | "body": "aa", 66 | "id": 13 67 | }, 68 | { 69 | "title": "aaa", 70 | "body": "aaaa", 71 | "id": 14 72 | }, 73 | { 74 | "title": "a", 75 | "body": "a", 76 | "id": 15 77 | } 78 | ] 79 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-query-yt", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "react-scripts start", 7 | "build": "react-scripts build", 8 | "test": "react-scripts test", 9 | "eject": "react-scripts eject", 10 | "server": "json-server --watch db.json --delay 1500 --port 3005" 11 | }, 12 | "dependencies": { 13 | "@testing-library/jest-dom": "^5.16.1", 14 | "@testing-library/react": "^12.1.2", 15 | "@testing-library/user-event": "^13.5.0", 16 | "axios": "^0.24.0", 17 | "react": "^17.0.2", 18 | "react-dom": "^17.0.2", 19 | "react-query": "^3.34.7", 20 | "react-scripts": "5.0.0", 21 | "web-vitals": "^2.1.2" 22 | }, 23 | "devDependencies": { 24 | "json-server": "^0.17.0" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymulenll/react-query-tutorial/b4c5ad967733d90cd848d150b8858b56f74c87fb/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | 28 | 34 | React Query - Demo 35 | 36 | 37 | 38 |
39 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymulenll/react-query-tutorial/b4c5ad967733d90cd848d150b8858b56f74c87fb/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ymulenll/react-query-tutorial/b4c5ad967733d90cd848d150b8858b56f74c87fb/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/anchor-is-valid */ 2 | import { useState } from "react"; 3 | import NewPost from "./components/NewPost"; 4 | import Post from "./components/Post"; 5 | import Posts from "./components/Posts"; 6 | 7 | export default function App() { 8 | const [postId, setPostId] = useState(-1); 9 | 10 | return ( 11 |
12 |

React-Query Demo

13 | {postId > -1 && ( 14 |
15 | setPostId(-1)} href="#"> 16 | Back 17 | 18 |
19 | )} 20 | {postId > -1 ? ( 21 | 22 | ) : ( 23 |
24 |
25 | 26 |
27 |
28 | 29 |
30 |
31 | )} 32 |
33 | ); 34 | } 35 | -------------------------------------------------------------------------------- /src/api/posts.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const API = process.env.REACT_APP_API || "http://localhost:3005"; 4 | 5 | export const getPostById = async (postId) => { 6 | const { data } = await axios.get(`${API}/posts/${postId}`); 7 | return data; 8 | }; 9 | 10 | export const getPosts = async () => { 11 | const { data } = await axios.get(`${API}/posts`); 12 | return data; 13 | }; 14 | 15 | export const createNewPost = async (post) => { 16 | const { data } = await axios.post(`${API}/posts`, post); 17 | return data; 18 | }; 19 | -------------------------------------------------------------------------------- /src/components/NewPost.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { useMutatePost } from "../hooks/posts"; 3 | 4 | function NewPost() { 5 | const [title, setTitle] = useState(""); 6 | const [body, setBody] = useState(""); 7 | 8 | const { mutate, error, isLoading, isSuccess, reset } = useMutatePost(); 9 | 10 | const handleSubmit = async (e) => { 11 | e.preventDefault(); 12 | 13 | mutate( 14 | { title, body }, 15 | { 16 | onSuccess: () => { 17 | setTitle(""); 18 | setBody(""); 19 | }, 20 | } 21 | ); 22 | }; 23 | 24 | return ( 25 |
26 |

Create Post:

27 |
28 |
29 | 32 | setTitle(e.target.value)} 35 | id="title" 36 | className="form-control" 37 | /> 38 |
39 |
40 | 43 | 50 |
51 | 52 | 62 | {error && ( 63 |

64 | Error creating the post: {error.message} 65 |

66 | )} 67 | {isSuccess && ( 68 |
69 | The post was saved successfuly 70 | 75 |
76 | )} 77 |
78 |
79 | ); 80 | } 81 | 82 | export default NewPost; 83 | -------------------------------------------------------------------------------- /src/components/Post.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/anchor-is-valid */ 2 | import { usePost } from "../hooks/posts"; 3 | 4 | export default function Post({ postId }) { 5 | const { data: post, error, isLoading } = usePost(postId); 6 | 7 | if (isLoading) { 8 | return ( 9 |
10 | Loading Post... 11 |
12 | ); 13 | } 14 | 15 | if (error) { 16 | return ( 17 |
18 | Error fetching post: {error.message} 19 |
20 | ); 21 | } 22 | 23 | return ( 24 |
25 |

{post.title}

26 |

{post.body}

27 |
28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /src/components/Posts.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable jsx-a11y/anchor-is-valid */ 2 | import { useQueryClient } from "react-query"; 3 | import { usePosts } from "../hooks/posts"; 4 | 5 | export default function Posts({ setPostId }) { 6 | const queryClient = useQueryClient(); 7 | const { 8 | data: posts, 9 | error, 10 | isLoading, 11 | isFetching, 12 | // isIdle, 13 | // refetch, 14 | } = usePosts(); 15 | 16 | // if (isIdle) { 17 | // return ; 18 | // } 19 | 20 | if (isLoading) { 21 | return ( 22 |
23 | Loading Posts... 24 |
25 | ); 26 | } 27 | 28 | if (error) { 29 | return ( 30 |
31 | Error fetching posts: {error.message} 32 |
33 | ); 34 | } 35 | 36 | return ( 37 |
38 |

Posts: {isFetching && }

39 | 54 |
55 | ); 56 | } 57 | -------------------------------------------------------------------------------- /src/hooks/posts.js: -------------------------------------------------------------------------------- 1 | import { useMutation, useQuery, useQueryClient } from "react-query"; 2 | import { createNewPost, getPostById, getPosts } from "../api/posts"; 3 | 4 | const key = "posts"; 5 | 6 | export function useMutatePost() { 7 | const queryClient = useQueryClient(); 8 | 9 | return useMutation(createNewPost, { 10 | onSuccess: (post) => { 11 | queryClient.setQueryData([key], (prevPosts) => prevPosts.concat(post)); 12 | queryClient.invalidateQueries([key]); 13 | }, 14 | }); 15 | } 16 | 17 | export function usePosts() { 18 | return useQuery([key], getPosts); 19 | } 20 | 21 | export function usePost(postId) { 22 | return useQuery([key, postId], () => getPostById(postId)); 23 | } 24 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from "react-dom"; 2 | import { QueryClient, QueryClientProvider } from "react-query"; 3 | import App from "./App"; 4 | import { ReactQueryDevtools } from "react-query/devtools"; 5 | 6 | const queryClient = new QueryClient(); 7 | 8 | ReactDOM.render( 9 | 10 | 11 | 12 | , 13 | document.getElementById("root") 14 | ); 15 | --------------------------------------------------------------------------------