├── .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 |
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 |
11 | {props.items.map((task) => (
12 | {task.text}
13 | ))}
14 |
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 |
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 ;
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 |
--------------------------------------------------------------------------------