├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── NewTask │ ├── NewTask.js │ ├── TaskForm.js │ └── TaskForm.module.css ├── Tasks │ ├── TaskItem.js │ ├── TaskItem.module.css │ ├── Tasks.js │ └── Tasks.module.css └── UI │ ├── Section.js │ └── Section.module.css ├── hooks └── use-http.js ├── index.css └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-custom-hook 2 | 3 | I hope this repo serves as a guide for other programmers to write custom hooks via axios to send requests to the database in React. 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-complete-guide", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.6", 7 | "@testing-library/react": "^11.2.2", 8 | "@testing-library/user-event": "^12.5.0", 9 | "axios": "^0.26.1", 10 | "react": "^18.0.0", 11 | "react-dom": "^18.0.0", 12 | "react-scripts": "^5.0.1", 13 | "web-vitals": "^0.2.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kamol-ayyub/react-custom-hook/0e86c41a178eeb4b2116ed03e0efd740dbd7c4d4/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kamol-ayyub/react-custom-hook/0e86c41a178eeb4b2116ed03e0efd740dbd7c4d4/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kamol-ayyub/react-custom-hook/0e86c41a178eeb4b2116ed03e0efd740dbd7c4d4/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 | import React, { useEffect, useState, useCallback } from 'react'; 2 | 3 | import Tasks from './components/Tasks/Tasks'; 4 | import NewTask from './components/NewTask/NewTask'; 5 | import useHttp from './hooks/use-http'; 6 | 7 | function App() { 8 | const [tasks, setTasks] = useState([]); 9 | 10 | const httpData = useHttp(); 11 | 12 | const { error, isLoading, sendRequest: fetchTasks } = httpData; // destructuring states from useHttp custom hook 13 | 14 | useEffect(() => { 15 | const transformTasks = (tasksObj) => { 16 | //transform tasks is actually applyData, from useHttp hook, for set data from response 17 | const loadedTasks = []; 18 | 19 | for (const taskKey in tasksObj) { 20 | loadedTasks.push({ id: taskKey, text: tasksObj[taskKey].text }); 21 | } 22 | 23 | setTasks(loadedTasks); 24 | }; 25 | fetchTasks( 26 | { 27 | url: '/tasks.json', 28 | }, 29 | transformTasks 30 | ); 31 | }, []); 32 | 33 | const taskAddHandler = (task) => { 34 | setTasks((prevTasks) => prevTasks.concat(task)); 35 | }; 36 | 37 | return ( 38 | 39 | 40 | 46 | 47 | ); 48 | } 49 | 50 | export default App; 51 | -------------------------------------------------------------------------------- /src/components/NewTask/NewTask.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | import Section from '../UI/Section'; 4 | import TaskForm from './TaskForm'; 5 | import useHttp from '../../hooks/use-http'; 6 | 7 | const NewTask = (props) => { 8 | const { error, isLoading, sendRequest: sendTaskRequest } = useHttp(); 9 | 10 | const createtask = (taskText, taskData) => { 11 | const generatedId = taskData.name; 12 | 13 | const createdTask = { id: generatedId, text: taskText }; 14 | 15 | props.onAddTask(createdTask); 16 | }; 17 | 18 | const enterTaskHandler = async (taskText) => { 19 | sendTaskRequest( 20 | { 21 | url: '/tasks.json', 22 | method: 'POST', 23 | data: { text: taskText }, 24 | }, 25 | createtask.bind(null, taskText) 26 | ); 27 | }; 28 | 29 | return ( 30 |
31 | 32 | {error &&

{error}

} 33 |
34 | ); 35 | }; 36 | 37 | export default NewTask; 38 | -------------------------------------------------------------------------------- /src/components/NewTask/TaskForm.js: -------------------------------------------------------------------------------- 1 | import { useRef } from 'react'; 2 | 3 | import classes from './TaskForm.module.css'; 4 | 5 | const TaskForm = (props) => { 6 | const taskInputRef = useRef(); 7 | 8 | const submitHandler = (event) => { 9 | event.preventDefault(); 10 | 11 | const enteredValue = taskInputRef.current.value; 12 | 13 | if (enteredValue.trim().length > 0) { 14 | props.onEnterTask(enteredValue); 15 | } 16 | }; 17 | 18 | return ( 19 |
20 | 21 | 22 |
23 | ); 24 | }; 25 | 26 | export default TaskForm; 27 | -------------------------------------------------------------------------------- /src/components/NewTask/TaskForm.module.css: -------------------------------------------------------------------------------- 1 | .form { 2 | display: flex; 3 | justify-content: space-between; 4 | } 5 | 6 | .form input { 7 | font:inherit; 8 | padding: 0.25rem; 9 | border: none; 10 | border-bottom: 3px solid #ccc; 11 | flex: 1; 12 | margin-right: 2rem; 13 | } 14 | 15 | .form input:focus { 16 | outline: none; 17 | border-color: #7a0144; 18 | } -------------------------------------------------------------------------------- /src/components/Tasks/TaskItem.js: -------------------------------------------------------------------------------- 1 | import classes from './TaskItem.module.css'; 2 | 3 | const TaskItem = (props) => { 4 | return
  • {props.children}
  • 5 | }; 6 | 7 | export default TaskItem; -------------------------------------------------------------------------------- /src/components/Tasks/TaskItem.module.css: -------------------------------------------------------------------------------- 1 | .task { 2 | border-bottom: 1px solid #ccc; 3 | padding: 1rem; 4 | font-weight: bold; 5 | font-size: 1.25rem; 6 | } 7 | 8 | .task:last-of-type { 9 | border-bottom: none; 10 | } -------------------------------------------------------------------------------- /src/components/Tasks/Tasks.js: -------------------------------------------------------------------------------- 1 | import Section from '../UI/Section'; 2 | import TaskItem from './TaskItem'; 3 | import classes from './Tasks.module.css'; 4 | 5 | const Tasks = (props) => { 6 | let taskList =

    No tasks found. Start adding some!

    ; 7 | 8 | if (props.items.length > 0) { 9 | taskList = ( 10 | 15 | ); 16 | } 17 | 18 | let content = taskList; 19 | 20 | if (props.error) { 21 | content = ; 22 | } 23 | 24 | if (props.loading) { 25 | content = 'Loading tasks...'; 26 | } 27 | 28 | return ( 29 |
    30 |
    {content}
    31 |
    32 | ); 33 | }; 34 | 35 | export default Tasks; 36 | -------------------------------------------------------------------------------- /src/components/Tasks/Tasks.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | text-align: center; 3 | } 4 | 5 | .container ul { 6 | list-style: none; 7 | margin: 0; 8 | padding: 0; 9 | text-align: left; 10 | } 11 | -------------------------------------------------------------------------------- /src/components/UI/Section.js: -------------------------------------------------------------------------------- 1 | import classes from './Section.module.css'; 2 | 3 | const Section = (props) => { 4 | return
    {props.children}
    ; 5 | }; 6 | 7 | export default Section; 8 | -------------------------------------------------------------------------------- /src/components/UI/Section.module.css: -------------------------------------------------------------------------------- 1 | .section { 2 | max-width: 40rem; 3 | margin: 2rem auto; 4 | background-color: white; 5 | padding: 1rem; 6 | border-radius: 12px; 7 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25); 8 | } 9 | -------------------------------------------------------------------------------- /src/hooks/use-http.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from 'react'; 2 | import axios from 'axios'; 3 | 4 | const api = axios.create({ 5 | baseURL: 'https://your-database-link.firebasedatabase.app', // replase your database link 6 | }); 7 | 8 | const useHttp = () => { 9 | const [isLoading, setIsLoading] = useState(false); 10 | const [error, setError] = useState(null); 11 | 12 | const sendRequest = useCallback(async (requestConfig, applyData) => { 13 | // requestConfig is object, for url, method and data's body 14 | setIsLoading(true); 15 | setError(null); 16 | try { 17 | const response = await api(requestConfig); 18 | 19 | const data = response.data; 20 | applyData(data); 21 | } catch (err) { 22 | setError(err.message || 'Something went wrong!'); 23 | } 24 | setIsLoading(false); 25 | }, []); 26 | return { error, isLoading, sendRequest }; 27 | }; 28 | export default useHttp; 29 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;700&display=swap'); 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | html { 8 | font-family: 'Noto Sans JP', sans-serif; 9 | } 10 | 11 | body { 12 | margin: 0; 13 | background-color: #3f3f3f; 14 | } 15 | 16 | button { 17 | cursor: pointer; 18 | font: inherit; 19 | background-color: #7a0144; 20 | border: 1px solid #7a0144; 21 | border-radius: 20px; 22 | padding: 0.5rem 2rem; 23 | color: white; 24 | } 25 | 26 | button:hover, 27 | button:active { 28 | background-color: #9c095a; 29 | border-color: #9c095a; 30 | } 31 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client'; 2 | 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render(); 8 | --------------------------------------------------------------------------------