├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package-lock.json ├── package.json ├── postcss.config.js ├── public └── vite.svg ├── src ├── App.css ├── App.jsx ├── assets │ └── react.svg ├── components │ ├── AddUserForm.jsx │ └── UserLists.jsx ├── index.css ├── main.jsx ├── req-processor │ └── requestProcessor.js └── utils │ └── axios.js ├── tailwind.config.js └── vite.config.js /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /.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 | # React + Vite 2 | 3 | This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. 4 | 5 | Currently, two official plugins are available: 6 | 7 | - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh 8 | - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh 9 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-query-demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "axios": "^1.5.1", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0", 16 | "react-query": "^3.39.3" 17 | }, 18 | "devDependencies": { 19 | "@types/react": "^18.2.15", 20 | "@types/react-dom": "^18.2.7", 21 | "@vitejs/plugin-react": "^4.0.3", 22 | "autoprefixer": "^10.4.16", 23 | "eslint": "^8.45.0", 24 | "eslint-plugin-react": "^7.32.2", 25 | "eslint-plugin-react-hooks": "^4.6.0", 26 | "eslint-plugin-react-refresh": "^0.4.3", 27 | "postcss": "^8.4.31", 28 | "tailwindcss": "^3.3.3", 29 | "vite": "^4.4.5" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /public/vite.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | /* Reset some default styles */ 2 | html, 3 | body, 4 | div, 5 | h1, 6 | h2, 7 | h3, 8 | p, 9 | ul, 10 | li, 11 | button { 12 | margin: 0; 13 | padding: 0; 14 | box-sizing: border-box; 15 | } 16 | 17 | /* Set up a background color and font */ 18 | body { 19 | font-family: Arial, sans-serif; 20 | background-color: #f7fafc; 21 | } 22 | 23 | /* Container for the entire app */ 24 | .container { 25 | max-width: 800px; 26 | margin: 0 auto; 27 | padding: 20px; 28 | } 29 | 30 | /* Header styles */ 31 | h1 { 32 | font-size: 2rem; 33 | font-weight: 700; 34 | margin-bottom: 20px; 35 | } 36 | 37 | /* Item card styles */ 38 | .item-card { 39 | border: 1px solid #e2e8f0; 40 | border-radius: 0.5rem; 41 | padding: 1rem; 42 | margin: 0.5rem; 43 | background-color: #fff; 44 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 45 | } 46 | 47 | .item-card h3 { 48 | font-size: 1.5rem; 49 | font-weight: 600; 50 | margin-bottom: 0.5rem; 51 | } 52 | 53 | .item-card p { 54 | font-size: 1rem; 55 | color: #4a5568; 56 | } 57 | 58 | /* Button styles */ 59 | .button { 60 | display: inline-block; 61 | padding: 0.5rem 1rem; 62 | border: none; 63 | border-radius: 0.25rem; 64 | cursor: pointer; 65 | transition: background-color 0.2s; 66 | } 67 | 68 | .button-primary { 69 | background-color: #4c51bf; 70 | color: #ffffff; 71 | } 72 | 73 | .button-primary:hover { 74 | background-color: #4338ca; 75 | } 76 | 77 | .button-danger { 78 | background-color: #e53e3e; 79 | color: #ffffff; 80 | } 81 | 82 | .button-danger:hover { 83 | background-color: #c53030; 84 | } 85 | 86 | /* Form styles */ 87 | .form { 88 | background-color: #ffffff; 89 | border: 1px solid #e2e8f0; 90 | border-radius: 0.5rem; 91 | padding: 1rem; 92 | margin: 1rem 0; 93 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 94 | } 95 | 96 | .form h2 { 97 | font-size: 1.5rem; 98 | font-weight: 600; 99 | margin-bottom: 1rem; 100 | } 101 | 102 | .form input[type="text"] { 103 | width: 100%; 104 | padding: 0.5rem; 105 | border: 1px solid #cbd5e0; 106 | border-radius: 0.25rem; 107 | font-size: 1rem; 108 | margin-bottom: 1rem; 109 | } 110 | 111 | /* Create button styles */ 112 | .create-button { 113 | background-color: #48bb78; 114 | color: #ffffff; 115 | } 116 | 117 | .create-button:hover { 118 | background-color: #38a169; 119 | } 120 | 121 | /* Update button styles */ 122 | .update-button { 123 | background-color: #4299e1; 124 | color: #ffffff; 125 | } 126 | 127 | .update-button:hover { 128 | background-color: #3182ce; 129 | } 130 | -------------------------------------------------------------------------------- /src/App.jsx: -------------------------------------------------------------------------------- 1 | import { QueryClient, QueryClientProvider } from "react-query"; 2 | import AddUserForm from "./components/AddUserForm"; 3 | import UserLists from "./components/UserLists"; 4 | 5 | const queryClient = new QueryClient(); 6 | 7 | const App = () => { 8 | return ( 9 |
10 | 11 | 12 | 13 | 14 |
15 | ); 16 | }; 17 | 18 | export default App; 19 | -------------------------------------------------------------------------------- /src/assets/react.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/components/AddUserForm.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | import { useRequestProcessor } from "../req-processor/requestProcessor"; 3 | import axiosClient from "../utils/axios"; 4 | 5 | function AddUserForm() { 6 | const { mutate } = useRequestProcessor(); 7 | const [name, setName] = useState(""); 8 | 9 | const handleSubmit = (event) => { 10 | event.preventDefault(); 11 | 12 | mutate("addUser", () => 13 | axiosClient.post("/photos", { name }).then((res) => res.data) 14 | ); 15 | setName(""); 16 | }; 17 | 18 | return ( 19 |
20 | 28 | 29 |
30 | ); 31 | } 32 | 33 | export default AddUserForm; 34 | -------------------------------------------------------------------------------- /src/components/UserLists.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useRequestProcessor } from "../req-processor/requestProcessor"; 3 | import axiosClient from "../utils/axios"; 4 | 5 | export default function UserLists() { 6 | const { query } = useRequestProcessor(); 7 | const { 8 | data: users, 9 | isLoading, 10 | isError, 11 | } = query("users", () => axiosClient.get("/users").then((res) => res.data), { 12 | enabled: true, 13 | }); 14 | if (isLoading) return

Loading...

; 15 | if (isError) return

Error

; 16 | return ( 17 | 29 | ); 30 | } 31 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /src/req-processor/requestProcessor.js: -------------------------------------------------------------------------------- 1 | import { useQuery, useMutation, useQueryClient } from "react-query"; 2 | 3 | export function useRequestProcessor() { 4 | const queryClient = useQueryClient(); 5 | 6 | function query(key, queryFunction, options = {}) { 7 | return useQuery({ 8 | queryKey: key, 9 | queryFn: queryFunction, 10 | ...options, 11 | }); 12 | } 13 | 14 | function mutate(key, mutationFunction, options = {}) { 15 | return useMutation({ 16 | mutationKey: key, 17 | mutationFn: mutationFunction, 18 | onSettled: () => queryClient.invalidateQueries(key), 19 | ...options, 20 | }); 21 | } 22 | 23 | return { query, mutate }; 24 | } 25 | -------------------------------------------------------------------------------- /src/utils/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const axiosClient = axios.create({ 4 | baseURL: "https://jsonplaceholder.typicode.com/", 5 | }); 6 | 7 | export default axiosClient; 8 | -------------------------------------------------------------------------------- /tailwind.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('tailwindcss').Config} */ 2 | export default { 3 | content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"], 4 | theme: { 5 | extend: {}, 6 | }, 7 | plugins: [], 8 | }; 9 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------