├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── TodoForm.js ├── Todos.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-todo-functional 2 | 3 | A simple ToDo application built with React using only functional components and no class components. This exercise was intended to explore and learn more about Hooks in React. 4 | 5 | [This project has been deployed and can be seen at this link.](https://heuristic-sammet-ab37a3.netlify.com/) 6 | 7 | Side Note: This project was featured in a [YouTube video](https://youtu.be/CUGPbZ77OuQ?t=133) by "The Clever Programmer" channel. 8 | 9 | ### Some notes about functional components in React 10 | 11 | * Functional components are good for presentational components (components that are stateless are just UI elements) 12 | * Functional components receive props as arguments and return JSX that is to be rendered. (We destructure the props in arguments using ES6 object destrucuturing for convenience) 13 | * They are also easier to work with (considering you don't have to worry about `this`) and easier to understand. 14 | 15 | Earlier, functional components were strictly stateless, but now, we can use React Hooks to implement the state functionality in functional components. 16 | 17 | ### Available Scripts 18 | 19 | This project was bootstrapped with create-react-app. In the project directory, you can run: 20 | 21 | ### `npm start` 22 | 23 | Runs the app in the development mode.
24 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 25 | 26 | The page will reload if you make edits.
27 | You will also see any lint errors in the console. 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-todo-functional", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.4.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "bootstrap": "^4.4.1", 10 | "react": "^16.12.0", 11 | "react-dom": "^16.12.0", 12 | "react-scripts": "3.3.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajrajhans/react-todo-functional/9d7f3cc9617a32828b8988b56d634f4d907c84ce/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Another ToDo App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajrajhans/react-todo-functional/9d7f3cc9617a32828b8988b56d634f4d907c84ce/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rajrajhans/react-todo-functional/9d7f3cc9617a32828b8988b56d634f4d907c84ce/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 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css?family=Lato&display=swap'); 2 | *{ 3 | margin: 0; 4 | padding: 0; 5 | border: 0; 6 | font-family: 'Lato', sans-serif; 7 | } 8 | 9 | .parent-container{ 10 | background: #0f0c29; /* fallback for old browsers */ 11 | background: -webkit-linear-gradient(to right, #24243e, #302b63, #0f0c29); /* Chrome 10-25, Safari 5.1-6 */ 12 | background: linear-gradient(to right, #24243e, #302b63, #0f0c29); /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */ 13 | height: 100vh; 14 | } 15 | 16 | .top-container{ 17 | padding-top: 15px; 18 | max-width: 600px; 19 | background: #f0eded; 20 | height: 100vh; 21 | } 22 | 23 | /*TODOFORM STYLES START*/ 24 | 25 | .app-title{ 26 | font-family: 'Lato', sans-serif; 27 | font-size: 36px; 28 | border: 1px solid #0e0e0e; 29 | margin: 0 auto 10px auto; 30 | background: #ffffff; 31 | box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 2px rgba(0,0,0,0.23); 32 | max-width: 400px; 33 | } 34 | 35 | .todo-form{ 36 | max-width: 400px; 37 | margin: 15px auto 10px auto; 38 | } 39 | /*TODOFORM STYLES END*/ 40 | 41 | /*TODOLIST STYLE START*/ 42 | .todo-list { 43 | margin: 0 auto 0 auto; 44 | background: #e8e8e8; 45 | border-radius: 4px; 46 | padding: 5px; 47 | max-width: 400px; 48 | } 49 | 50 | .todo { 51 | background: #fff; 52 | box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.15); 53 | padding: 3px 10px; 54 | font-size: 16px; 55 | margin-bottom: 6px; 56 | border-radius: 3px; 57 | display: flex; 58 | align-items: center; 59 | justify-content: space-between; 60 | transition: 0.2s ease; 61 | } 62 | 63 | .todo:hover{ 64 | background: #dbdbdb; 65 | } 66 | 67 | .todo-text{ 68 | margin-left:0; 69 | } 70 | /*TODOLIST STYLE END*/ 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react'; 2 | import './App.css'; 3 | import TodoForm from "./TodoForm"; 4 | import Todos from "./Todos"; 5 | 6 | function App() { 7 | const [todos, setTodos] = useState([ 8 | { 9 | id:1, 10 | text: "Write a new blog post.", 11 | isCompleted: false 12 | }, 13 | { 14 | id:2, 15 | text: "Pick up laundry.", 16 | isCompleted: false 17 | }, 18 | { 19 | id:3, 20 | text: "Die.", 21 | isCompleted: false 22 | } 23 | ]); 24 | 25 | const addTodo = (todo) => { 26 | setTodos([...todos, {text:todo}] ); 27 | }; 28 | 29 | const markComplete = (index) => { 30 | const newTodos = [...todos]; 31 | newTodos[index].isCompleted = !newTodos[index].isCompleted; 32 | setTodos(newTodos); 33 | }; 34 | 35 | const editTitle = (index, title) => { 36 | const newTodos = [...todos]; 37 | newTodos[index].text = title; 38 | setTodos(newTodos); 39 | }; 40 | 41 | return ( 42 |
43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 |
52 | ); 53 | } 54 | 55 | export default App; 56 | -------------------------------------------------------------------------------- /src/TodoForm.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from 'react' 2 | 3 | const TodoForm = ({addTodo}) =>{ 4 | const [value, setvalue] = useState(''); 5 | 6 | const handleChange = (e) => { //to handle change in form input (when user is typing) 7 | setvalue(e.target.value); //e.target gives the value of current value the user is typing 8 | }; 9 | 10 | const handleSubmit = (e) => { 11 | e.preventDefault(); //to prevent DOM from reloading the page 12 | if (!value) //if user submits empty form, do nothing 13 | return; 14 | addTodo(value); 15 | setvalue(''); //set the value to be empty again to get ready for next input 16 | }; 17 | 18 | return( 19 |
20 |
Another ToDo App
21 |
22 | 23 | 24 |
25 |
26 | ) 27 | }; 28 | 29 | export default TodoForm; -------------------------------------------------------------------------------- /src/Todos.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Todos = ({todos, markComplete, editTitle}) => { 4 | return ( 5 |
6 | { 7 | todos.map((todo, index) => ( 8 | 9 | )) 10 | } 11 |
12 | ); 13 | }; 14 | 15 | const Todo = ({todo, index, markComplete, editTitle}) =>( 16 |
17 |

18 | markComplete(index)} name={"completed"} id={todo.id}/>{' '} 19 | {todo.text} 20 |

21 |
22 | ); 23 | 24 | export default Todos; 25 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import 'bootstrap/dist/css/bootstrap.css'; 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import App from './App'; 5 | 6 | ReactDOM.render(, document.getElementById('root')); 7 | 8 | // If you want your app to work offline and load faster, you can change 9 | // unregister() to register() below. Note this comes with some pitfalls. 10 | // Learn more about service workers: https://bit.ly/CRA-PWA 11 | --------------------------------------------------------------------------------