├── .gitignore ├── images ├── flux.png ├── logo.jpeg ├── phases.png ├── state.jpg ├── vdom1.png ├── vdom2.png ├── vdom3.png ├── devtoolsTab.png ├── phases16.3.jpg ├── phases16.4.png ├── error_boundary.png ├── collab │ ├── BFCM2025.gif │ ├── resumeloom.png │ ├── greatfrontend-react.gif │ └── greatfrontend-banner4x.png └── devtoolsInspect.png ├── coding-exercise ├── public │ ├── robots.txt │ ├── favicon.ico │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── index.html ├── src │ ├── exercises │ │ ├── __template__ │ │ │ ├── index.js │ │ │ ├── Problem.js │ │ │ ├── README.md │ │ │ └── Solution.js │ │ ├── exercise-01-state-batching │ │ │ ├── index.js │ │ │ ├── Problem.js │ │ │ └── Solution.js │ │ ├── exercise-04-custom-hooks │ │ │ ├── index.js │ │ │ ├── Problem.js │ │ │ └── Solution.js │ │ ├── exercise-02-useeffect-dependencies │ │ │ ├── index.js │ │ │ ├── Problem.js │ │ │ └── Solution.js │ │ ├── exercise-03-useCallback-memoization │ │ │ ├── index.js │ │ │ ├── Problem.js │ │ │ └── Solution.js │ │ └── index.js │ ├── setupTests.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── App.css │ ├── components │ │ ├── NavigationButton.js │ │ ├── HomePage.js │ │ └── ExerciseCard.js │ ├── App.js │ ├── logo.svg │ └── serviceWorker.js ├── .gitignore ├── package.json ├── QUICK_START.md ├── ARCHITECTURE.md └── README.md ├── .github └── FUNDING.yml └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | # Cruft 2 | .DS_Store 3 | npm-debug.log 4 | .idea -------------------------------------------------------------------------------- /images/flux.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/flux.png -------------------------------------------------------------------------------- /images/logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/logo.jpeg -------------------------------------------------------------------------------- /images/phases.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/phases.png -------------------------------------------------------------------------------- /images/state.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/state.jpg -------------------------------------------------------------------------------- /images/vdom1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/vdom1.png -------------------------------------------------------------------------------- /images/vdom2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/vdom2.png -------------------------------------------------------------------------------- /images/vdom3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/vdom3.png -------------------------------------------------------------------------------- /coding-exercise/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /images/devtoolsTab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/devtoolsTab.png -------------------------------------------------------------------------------- /images/phases16.3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/phases16.3.jpg -------------------------------------------------------------------------------- /images/phases16.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/phases16.4.png -------------------------------------------------------------------------------- /images/error_boundary.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/error_boundary.png -------------------------------------------------------------------------------- /images/collab/BFCM2025.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/collab/BFCM2025.gif -------------------------------------------------------------------------------- /images/collab/resumeloom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/collab/resumeloom.png -------------------------------------------------------------------------------- /images/devtoolsInspect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/devtoolsInspect.png -------------------------------------------------------------------------------- /coding-exercise/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/coding-exercise/public/favicon.ico -------------------------------------------------------------------------------- /coding-exercise/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/coding-exercise/public/logo192.png -------------------------------------------------------------------------------- /coding-exercise/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/coding-exercise/public/logo512.png -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [sudheerj] 4 | custom: https://buymeacoffee.com/sudheerj 5 | 6 | -------------------------------------------------------------------------------- /images/collab/greatfrontend-react.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/collab/greatfrontend-react.gif -------------------------------------------------------------------------------- /images/collab/greatfrontend-banner4x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sudheerj/reactjs-interview-questions/HEAD/images/collab/greatfrontend-banner4x.png -------------------------------------------------------------------------------- /coding-exercise/src/exercises/__template__/index.js: -------------------------------------------------------------------------------- 1 | export { default as Problem } from './Problem'; 2 | export { default as Solution } from './Solution'; 3 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-01-state-batching/index.js: -------------------------------------------------------------------------------- 1 | export { default as Problem } from './Problem'; 2 | export { default as Solution } from './Solution'; 3 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-04-custom-hooks/index.js: -------------------------------------------------------------------------------- 1 | export { default as Problem } from './Problem'; 2 | export { default as Solution } from './Solution'; 3 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-02-useeffect-dependencies/index.js: -------------------------------------------------------------------------------- 1 | export { default as Problem } from './Problem'; 2 | export { default as Solution } from './Solution'; 3 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-03-useCallback-memoization/index.js: -------------------------------------------------------------------------------- 1 | export { default as Problem } from './Problem'; 2 | export { default as Solution } from './Solution'; 3 | -------------------------------------------------------------------------------- /coding-exercise/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom/extend-expect'; 6 | -------------------------------------------------------------------------------- /coding-exercise/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { render } from '@testing-library/react'; 3 | import App from './App'; 4 | 5 | test('renders learn react link', () => { 6 | const { getByText } = render(); 7 | const linkElement = getByText(/learn react/i); 8 | expect(linkElement).toBeInTheDocument(); 9 | }); 10 | -------------------------------------------------------------------------------- /coding-exercise/.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 | -------------------------------------------------------------------------------- /coding-exercise/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /coding-exercise/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import * as serviceWorker from './serviceWorker'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want your app to work offline and load faster, you can change 15 | // unregister() to register() below. Note this comes with some pitfalls. 16 | // Learn more about service workers: https://bit.ly/CRA-PWA 17 | serviceWorker.unregister(); 18 | -------------------------------------------------------------------------------- /coding-exercise/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 | -------------------------------------------------------------------------------- /coding-exercise/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /coding-exercise/src/components/NavigationButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | function NavigationButton({ onClick, children, style = {} }) { 4 | return ( 5 | 26 | ); 27 | } 28 | 29 | export default NavigationButton; 30 | -------------------------------------------------------------------------------- /coding-exercise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coding-exercise", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.3.2", 8 | "@testing-library/user-event": "^7.1.2", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-scripts": "3.4.1" 12 | }, 13 | "scripts": { 14 | "start": "react-scripts start", 15 | "build": "react-scripts build", 16 | "test": "react-scripts test", 17 | "eject": "react-scripts eject" 18 | }, 19 | "eslintConfig": { 20 | "extends": "react-app" 21 | }, 22 | "browserslist": { 23 | "production": [ 24 | ">0.2%", 25 | "not dead", 26 | "not op_mini all" 27 | ], 28 | "development": [ 29 | "last 1 chrome version", 30 | "last 1 firefox version", 31 | "last 1 safari version" 32 | ] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-Present Sudheer Jonna 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /coding-exercise/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import './App.css'; 3 | import HomePage from './components/HomePage'; 4 | import NavigationButton from './components/NavigationButton'; 5 | import { exercises } from './exercises'; 6 | 7 | function App() { 8 | const [currentView, setCurrentView] = useState('home'); 9 | 10 | const renderView = () => { 11 | if (currentView === 'home') { 12 | return ; 13 | } 14 | 15 | // Parse the view to get exercise ID and type (problem/solution) 16 | const [exerciseId, viewType] = currentView.split('-').reduce((acc, part, idx, arr) => { 17 | if (idx === arr.length - 1) { 18 | return [acc[0], part]; // Last part is the view type 19 | } 20 | return [acc[0] ? `${acc[0]}-${part}` : part, acc[1]]; 21 | }, ['', '']); 22 | 23 | const exercise = exercises.find((ex) => ex.id === exerciseId); 24 | 25 | if (!exercise) { 26 | return
Exercise not found
; 27 | } 28 | 29 | const Component = viewType === 'solution' ? exercise.Solution : exercise.Problem; 30 | return ; 31 | }; 32 | 33 | return ( 34 |
35 | {currentView !== 'home' && ( 36 | setCurrentView('home')}> 37 | ← Back to Home 38 | 39 | )} 40 | {renderView()} 41 |
42 | ); 43 | } 44 | 45 | export default App; 46 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-01-state-batching/Problem.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 1: State Batching and Event Handlers 5 | * 6 | * PROBLEM: 7 | * What will be the output after clicking the "Increment" button once? 8 | * 9 | * Options: 10 | * A) Counter: 3, Alert shows: 3 11 | * B) Counter: 3, Alert shows: 0 12 | * C) Counter: 1, Alert shows: 0 13 | * D) Counter: 1, Alert shows: 1 14 | * 15 | * BONUS: How would you modify this to make the counter increment by 3? 16 | */ 17 | 18 | function Problem() { 19 | const [counter, setCounter] = useState(0); 20 | 21 | const handleIncrement = () => { 22 | setCounter(counter + 1); 23 | setCounter(counter + 1); 24 | setCounter(counter + 1); 25 | alert(`Counter value: ${counter}`); 26 | }; 27 | 28 | return ( 29 |
30 |

Exercise 1: State Batching Problem

31 |

Current Counter: {counter}

32 | 33 |
34 |

Question: What will happen when you click the button?

35 |

Think about:

36 |
    37 |
  • What value will the counter display?
  • 38 |
  • What value will the alert show?
  • 39 |
  • Why does this behavior occur?
  • 40 |
41 |
42 |
43 | ); 44 | } 45 | 46 | export default Problem; 47 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/__template__/Problem.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE X: [Exercise Title] 5 | * 6 | * PROBLEM: 7 | * [Describe the problem here] 8 | * 9 | * Options: 10 | * A) [Option A] 11 | * B) [Option B] 12 | * C) [Option C] 13 | * D) [Option D] 14 | * 15 | * BONUS: [Optional bonus question] 16 | */ 17 | 18 | function Problem() { 19 | // Add your state and logic here 20 | const [state, setState] = useState(0); 21 | 22 | return ( 23 |
24 |

Exercise X: [Exercise Title]

25 | 26 | {/* Your interactive component here */} 27 |

State: {state}

28 | 29 | 30 | {/* Problem description */} 31 |
38 |

⚠️ Question: What will happen when you interact with this component?

39 |

Think about:

40 |
    41 |
  • Question 1?
  • 42 |
  • Question 2?
  • 43 |
  • Question 3?
  • 44 |
45 |

46 | Tip: Open the browser console to see logs 47 |

48 |
49 |
50 | ); 51 | } 52 | 53 | export default Problem; 54 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-02-useeffect-dependencies/Problem.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 2: useEffect Dependencies and Infinite Loops 5 | * 6 | * PROBLEM: 7 | * What will happen when this component renders? 8 | * 9 | * Options: 10 | * A) The count increments once to 1 and stops 11 | * B) The count increments infinitely causing an infinite loop 12 | * C) The component throws an error 13 | * D) The count stays at 0 14 | * 15 | * BONUS: How would you fix this to increment only once on mount? 16 | */ 17 | 18 | function Problem() { 19 | const [count, setCount] = useState(0); 20 | const [data, setData] = useState({ value: 0 }); 21 | 22 | useEffect(() => { 23 | console.log('Effect running...'); 24 | setCount(count + 1); 25 | setData({ value: count }); 26 | }, [data]); 27 | 28 | return ( 29 |
30 |

Exercise 2: useEffect Dependencies Problem

31 |

Count: {count}

32 |

Data Value: {data.value}

33 |
34 |

⚠️ Warning: This component has a bug!

35 |

Question: What will happen when this component renders?

36 |
    37 |
  • Will it render once?
  • 38 |
  • Will it cause an infinite loop?
  • 39 |
  • Why does this behavior occur?
  • 40 |
  • How would you fix it?
  • 41 |
42 |

43 | Note: Check the browser console to see the effect running 44 |

45 |
46 |
47 | ); 48 | } 49 | 50 | export default Problem; 51 | -------------------------------------------------------------------------------- /coding-exercise/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 | -------------------------------------------------------------------------------- /coding-exercise/src/components/HomePage.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ExerciseCard from './ExerciseCard'; 3 | import { exercises } from '../exercises'; 4 | 5 | function HomePage({ onNavigate }) { 6 | return ( 7 |
8 |

React Coding Exercises

9 |

10 | Interactive coding exercises to test your React knowledge 11 |

12 | 13 |
14 | {exercises.map((exercise) => ( 15 | onNavigate(`${exercise.id}-problem`)} 19 | onViewSolution={() => onNavigate(`${exercise.id}-solution`)} 20 | /> 21 | ))} 22 |
23 | 24 |
25 |

💡 Tips for Success

26 |
    27 |
  • Try to solve the problem before viewing the solution
  • 28 |
  • Open the browser console to see effect logs
  • 29 |
  • Experiment with the code to deepen your understanding
  • 30 |
  • Read the explanations carefully to understand the "why"
  • 31 |
32 |
33 | 34 |
35 |

📝 Contributing

36 |

To add a new exercise:

37 |
    38 |
  1. Create a folder: exercises/exercise-XX-name/
  2. 39 |
  3. Add Problem.js and Solution.js
  4. 40 |
  5. Create index.js to export both components
  6. 41 |
  7. Register in exercises/index.js
  8. 42 |
43 |
44 |
45 | ); 46 | } 47 | 48 | export default HomePage; 49 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/index.js: -------------------------------------------------------------------------------- 1 | import * as Exercise01 from './exercise-01-state-batching'; 2 | import * as Exercise02 from './exercise-02-useeffect-dependencies'; 3 | import * as Exercise03 from './exercise-03-useCallback-memoization'; 4 | import * as Exercise04 from './exercise-04-custom-hooks'; 5 | 6 | /** 7 | * Exercise Registry 8 | * 9 | * To add a new exercise: 10 | * 1. Create a new folder: exercises/exercise-XX-name/ 11 | * 2. Add Problem.js and Solution.js files 12 | * 3. Create index.js to export both components 13 | * 4. Import and add to the exercises array below 14 | */ 15 | 16 | export const exercises = [ 17 | { 18 | id: 'exercise-01', 19 | title: 'State Batching & Closures', 20 | description: 'Learn about React state updates, closures, and functional updates', 21 | difficulty: 'Medium', 22 | topics: ['useState', 'Closures', 'State Batching', 'Event Handlers'], 23 | Problem: Exercise01.Problem, 24 | Solution: Exercise01.Solution, 25 | }, 26 | { 27 | id: 'exercise-02', 28 | title: 'useEffect Dependencies', 29 | description: 'Understand useEffect dependencies and avoid infinite loops', 30 | difficulty: 'Medium', 31 | topics: ['useEffect', 'Dependencies', 'Infinite Loops', 'useRef'], 32 | Problem: Exercise02.Problem, 33 | Solution: Exercise02.Solution, 34 | }, 35 | { 36 | id: 'exercise-03', 37 | title: 'useCallback & Memoization', 38 | description: 'Prevent unnecessary re-renders with useCallback and React.memo', 39 | difficulty: 'Medium', 40 | topics: ['useCallback', 'React.memo', 'Performance', 'Re-renders'], 41 | Problem: Exercise03.Problem, 42 | Solution: Exercise03.Solution, 43 | }, 44 | { 45 | id: 'exercise-04', 46 | title: 'Custom Hooks', 47 | description: 'Extract reusable logic with custom hooks for cleaner code', 48 | difficulty: 'Medium', 49 | topics: ['Custom Hooks', 'Code Reusability', 'DRY', 'Best Practices'], 50 | Problem: Exercise04.Problem, 51 | Solution: Exercise04.Solution, 52 | }, 53 | // Add more exercises here... 54 | ]; 55 | 56 | export default exercises; 57 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/__template__/README.md: -------------------------------------------------------------------------------- 1 | # Exercise Template 2 | 3 | This is a template folder for creating new exercises. **DO NOT** register this template in the exercise registry. 4 | 5 | ## How to Use This Template 6 | 7 | 1. **Copy this folder** and rename it: 8 | ``` 9 | cp -r __template__ exercise-XX-your-topic-name 10 | ``` 11 | 12 | 2. **Update Problem.js**: 13 | - Replace `[Exercise Title]` with your exercise title 14 | - Add your problem code and logic 15 | - Update the questions and options 16 | 17 | 3. **Update Solution.js**: 18 | - Replace `[Exercise Title]` with your exercise title 19 | - Add your solution code with explanations 20 | - Update all placeholder text 21 | 22 | 4. **Register in exercises/index.js**: 23 | ```javascript 24 | import * as ExerciseXX from './exercise-XX-your-topic-name'; 25 | 26 | export const exercises = [ 27 | // ... existing exercises 28 | { 29 | id: 'exercise-XX', 30 | title: 'Your Topic Name', 31 | description: 'Brief description', 32 | difficulty: 'Medium', 33 | topics: ['Topic1', 'Topic2'], 34 | Problem: ExerciseXX.Problem, 35 | Solution: ExerciseXX.Solution, 36 | }, 37 | ]; 38 | ``` 39 | 40 | 5. **Test your exercise**: 41 | ```bash 42 | npm start 43 | ``` 44 | 45 | ## Template Sections 46 | 47 | ### Problem.js 48 | - Exercise metadata (title, options, bonus) 49 | - Interactive component 50 | - Problem description with questions 51 | 52 | ### Solution.js 53 | - Answer and explanation 54 | - Problem demonstration (wrong approach) 55 | - Solution demonstration (correct approach) 56 | - Key takeaways 57 | - Code comparison 58 | - Common mistakes 59 | 60 | ## Styling Reference 61 | 62 | - Problem warning: `#fff3cd` with `#ffc107` border 63 | - Wrong approach: `#ffe6e6` background 64 | - Correct solution: `#e6ffe6` background 65 | - Information: `#e6f3ff` background 66 | - Key takeaways: `#f0f0f0` background 67 | 68 | ## Tips 69 | 70 | - Keep code examples concise and focused 71 | - Use emojis for visual hierarchy (❌ ✅ 💡 📚 ⚠️) 72 | - Add console.log statements for debugging 73 | - Test all interactive elements 74 | - Ensure responsive design with inline styles 75 | -------------------------------------------------------------------------------- /coding-exercise/src/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-03-useCallback-memoization/Problem.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 3: useCallback and Unnecessary Re-renders 5 | * 6 | * PROBLEM: 7 | * In the code below, the ChildComponent re-renders every time the parent's count changes, 8 | * even though the child only depends on the handleClick function. 9 | * 10 | * Options: 11 | * A) ChildComponent re-renders only when button is clicked 12 | * B) ChildComponent re-renders every time count changes 13 | * C) ChildComponent never re-renders 14 | * D) React throws an error about missing dependencies 15 | * 16 | * BONUS: How would you prevent unnecessary re-renders of ChildComponent? 17 | */ 18 | 19 | // Child component that should only re-render when its props change 20 | const ChildComponent = ({ onClick }) => { 21 | console.log('ChildComponent rendered'); 22 | return ( 23 |
24 |

I'm a child component

25 | 26 |
27 | ); 28 | }; 29 | 30 | function Problem() { 31 | const [count, setCount] = useState(0); 32 | const [childClicks, setChildClicks] = useState(0); 33 | 34 | // This function is recreated on every render 35 | const handleClick = () => { 36 | setChildClicks(prev => prev + 1); 37 | }; 38 | 39 | return ( 40 |
41 |

Exercise 3: useCallback & Memoization Problem

42 | 43 |
44 |

Parent Count: {count}

45 | 46 |
47 | 48 |
49 |

Child Clicks: {childClicks}

50 |
51 | 52 | 53 | 54 |
61 |

⚠️ Question: What happens when you click "Increment Parent Count"?

62 |

Think about:

63 |
    64 |
  • Does the ChildComponent re-render?
  • 65 |
  • Why does it re-render (or not)?
  • 66 |
  • How can you prevent unnecessary re-renders?
  • 67 |
  • Check the console to see render logs
  • 68 |
69 |

70 | Tip: Open the browser console and click the parent button multiple times 71 |

72 |
73 |
74 | ); 75 | } 76 | 77 | export default Problem; 78 | -------------------------------------------------------------------------------- /coding-exercise/src/components/ExerciseCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const difficultyColors = { 4 | Easy: '#28a745', 5 | Medium: '#ffc107', 6 | Hard: '#dc3545', 7 | }; 8 | 9 | function ExerciseCard({ exercise, onViewProblem, onViewSolution }) { 10 | return ( 11 |
20 |
21 |
22 |

{exercise.title}

23 |

{exercise.description}

24 | 25 |
26 | 38 | {exercise.difficulty} 39 | 40 | {exercise.topics.map((topic) => ( 41 | 54 | {topic} 55 | 56 | ))} 57 |
58 |
59 |
60 | 61 |
62 | 76 | 91 |
92 |
93 | ); 94 | } 95 | 96 | export default ExerciseCard; 97 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/__template__/Solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE X: [Exercise Title] - SOLUTION 5 | * 6 | * ANSWER: [Correct option] 7 | * 8 | * EXPLANATION: 9 | * 10 | * 1. [First key point] 11 | * - [Detail] 12 | * - [Detail] 13 | * 14 | * 2. [Second key point] 15 | * - [Detail] 16 | * - [Detail] 17 | * 18 | * 3. [Third key point] 19 | * - [Detail] 20 | */ 21 | 22 | function Solution() { 23 | // Example 1: The Problem 24 | const [problemState, setProblemState] = useState(0); 25 | 26 | // Example 2: The Solution 27 | const [solutionState, setSolutionState] = useState(0); 28 | 29 | return ( 30 |
31 |

Exercise X: [Exercise Title] - Solution

32 | 33 | {/* The Problem Section */} 34 |
40 |

❌ The Problem

41 |

State: {problemState}

42 | 45 |
 51 | {`// Problematic code
 52 | const [state, setState] = useState(0);
 53 | // ... problematic implementation`}
 54 |         
55 |

56 | Why it's wrong: [Explanation] 57 |

58 |
59 | 60 | {/* The Solution Section */} 61 |
67 |

✅ The Solution

68 |

State: {solutionState}

69 | 72 |
 78 | {`// Correct code
 79 | const [state, setState] = useState(0);
 80 | // ... correct implementation`}
 81 |         
82 |

83 | Why it works: [Explanation] 84 |

85 |
86 | 87 | {/* Key Takeaways Section */} 88 |
93 |

📚 Key Takeaways

94 |
    95 |
  1. Point 1: Explanation
  2. 96 |
  3. Point 2: Explanation
  4. 97 |
  5. Point 3: Explanation
  6. 98 |
  7. Point 4: Explanation
  8. 99 |
100 |
101 | 102 | {/* Code Comparison Section */} 103 |
109 |

💡 Code Comparison

110 |
116 | {`// ❌ WRONG
117 | // Wrong code example
118 | 
119 | // ✅ CORRECT
120 | // Correct code example`}
121 |         
122 |
123 | 124 | {/* Common Mistakes Section */} 125 |
131 |

⚠️ Common Mistakes

132 |
    133 |
  • ❌ Mistake 1
  • 134 |
  • ❌ Mistake 2
  • 135 |
  • ✅ Best practice 1
  • 136 |
  • ✅ Best practice 2
  • 137 |
138 |
139 |
140 | ); 141 | } 142 | 143 | export default Solution; 144 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-04-custom-hooks/Problem.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 4: Custom Hooks and Code Reusability 5 | * 6 | * PROBLEM: 7 | * You have two components that fetch data from different APIs. 8 | * Both components have similar logic for loading, error handling, and data fetching. 9 | * 10 | * Question: What's wrong with this code? 11 | * 12 | * Options: 13 | * A) Nothing is wrong, this is the correct way 14 | * B) Code duplication - should extract into a custom hook 15 | * C) Missing cleanup in useEffect 16 | * D) Should use fetch instead of hardcoded data 17 | * 18 | * BONUS: How would you create a reusable custom hook for this pattern? 19 | */ 20 | 21 | // Component 1: Fetches user data 22 | function UserProfile() { 23 | const [data, setData] = useState(null); 24 | const [loading, setLoading] = useState(true); 25 | const [error, setError] = useState(null); 26 | 27 | useEffect(() => { 28 | setLoading(true); 29 | // Simulating API call 30 | setTimeout(() => { 31 | try { 32 | setData({ name: 'John Doe', email: 'john@example.com' }); 33 | setLoading(false); 34 | } catch (err) { 35 | setError(err.message); 36 | setLoading(false); 37 | } 38 | }, 1000); 39 | }, []); 40 | 41 | if (loading) return
Loading user...
; 42 | if (error) return
Error: {error}
; 43 | return ( 44 |
45 |

User Profile

46 |

Name: {data?.name}

47 |

Email: {data?.email}

48 |
49 | ); 50 | } 51 | 52 | // Component 2: Fetches posts data (DUPLICATE LOGIC!) 53 | function PostsList() { 54 | const [data, setData] = useState(null); 55 | const [loading, setLoading] = useState(true); 56 | const [error, setError] = useState(null); 57 | 58 | useEffect(() => { 59 | setLoading(true); 60 | // Simulating API call 61 | setTimeout(() => { 62 | try { 63 | setData([ 64 | { id: 1, title: 'First Post' }, 65 | { id: 2, title: 'Second Post' } 66 | ]); 67 | setLoading(false); 68 | } catch (err) { 69 | setError(err.message); 70 | setLoading(false); 71 | } 72 | }, 1000); 73 | }, []); 74 | 75 | if (loading) return
Loading posts...
; 76 | if (error) return
Error: {error}
; 77 | return ( 78 |
79 |

Posts

80 | {data?.map(post => ( 81 |

• {post.title}

82 | ))} 83 |
84 | ); 85 | } 86 | 87 | function Problem() { 88 | return ( 89 |
90 |

Exercise 4: Custom Hooks Problem

91 | 92 |
93 | 94 | 95 |
96 | 97 |
104 |

⚠️ Question: What's the problem with this code?

105 |

Think about:

106 |
    107 |
  • Is there code duplication?
  • 108 |
  • How would you make this reusable?
  • 109 |
  • What would a custom hook look like?
  • 110 |
  • What are the benefits of extracting this logic?
  • 111 |
112 |

113 | Hint: Look at the similar patterns in both components 114 |

115 |
116 | 117 |
123 |

❌ Code Smell Detected

124 |
    125 |
  • Both components have identical state management (data, loading, error)
  • 126 |
  • Both have the same useEffect pattern
  • 127 |
  • Both have the same conditional rendering logic
  • 128 |
  • If we need to add a feature (e.g., retry), we'd have to update both!
  • 129 |
130 |
131 |
132 | ); 133 | } 134 | 135 | export default Problem; 136 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-01-state-batching/Solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 1: State Batching and Event Handlers - SOLUTION 5 | * 6 | * ANSWER: C) Counter: 1, Alert shows: 0 7 | * 8 | * EXPLANATION: 9 | * 10 | * 1. STATE CLOSURE: 11 | * - When the event handler runs, `counter` is captured with its current value (0) 12 | * - All three setCounter calls use the same captured value: counter + 1 = 0 + 1 = 1 13 | * - React batches these updates and only applies the last one 14 | * 15 | * 2. ALERT TIMING: 16 | * - The alert executes synchronously before React re-renders 17 | * - So it shows the old value (0), not the new value (1) 18 | * 19 | * 3. BATCHING: 20 | * - React batches multiple setState calls in event handlers for performance 21 | * - Since all three calls set the state to the same value (1), the result is just 1 22 | * 23 | * CORRECT APPROACH (Functional Updates): 24 | * Use the functional form of setState to access the previous state value 25 | */ 26 | 27 | function Solution() { 28 | const [counter, setCounter] = useState(0); 29 | const [correctCounter, setCorrectCounter] = useState(0); 30 | 31 | // WRONG: Uses stale closure value 32 | const handleIncrementWrong = () => { 33 | setCounter(counter + 1); 34 | setCounter(counter + 1); 35 | setCounter(counter + 1); 36 | alert(`Wrong approach - Counter value in alert: ${counter}`); 37 | }; 38 | 39 | // CORRECT: Uses functional updates 40 | const handleIncrementCorrect = () => { 41 | setCorrectCounter(prev => prev + 1); 42 | setCorrectCounter(prev => prev + 1); 43 | setCorrectCounter(prev => prev + 1); 44 | // Note: Alert still shows old value because state updates are async 45 | alert(`Correct approach - Counter value in alert: ${correctCounter} (old value)`); 46 | }; 47 | 48 | return ( 49 |
50 |

Exercise 1: State Batching Solution

51 | 52 |
53 |

❌ Wrong Approach (Closure Problem)

54 |

Current Counter: {counter}

55 | 56 |

57 | This only increments by 1 because all three setState calls use the same captured value. 58 |

59 |
60 | 61 |
62 |

✅ Correct Approach (Functional Updates)

63 |

Current Counter: {correctCounter}

64 | 65 |

66 | This increments by 3 because each setState receives the previous state value. 67 |

68 |
69 | 70 |
71 |

📚 Key Takeaways

72 |
    73 |
  1. State is a snapshot: The state value doesn't change during a render
  2. 74 |
  3. Closures capture values: Event handlers capture the state value from when they were created
  4. 75 |
  5. Use functional updates: When new state depends on previous state, use the function form
  6. 76 |
  7. Batching: React batches multiple setState calls in event handlers
  8. 77 |
  9. Async updates: State updates are asynchronous - you can't read the new value immediately
  10. 78 |
79 |
80 | 81 |
82 |

💡 Code Comparison

83 |
 84 | {`// ❌ WRONG - Uses stale closure
 85 | setCounter(counter + 1);  // 0 + 1 = 1
 86 | setCounter(counter + 1);  // 0 + 1 = 1
 87 | setCounter(counter + 1);  // 0 + 1 = 1
 88 | // Result: counter = 1
 89 | 
 90 | // ✅ CORRECT - Uses previous state
 91 | setCounter(prev => prev + 1);  // 0 + 1 = 1
 92 | setCounter(prev => prev + 1);  // 1 + 1 = 2
 93 | setCounter(prev => prev + 1);  // 2 + 1 = 3
 94 | // Result: counter = 3`}
 95 |         
96 |
97 |
98 | ); 99 | } 100 | 101 | export default Solution; 102 | -------------------------------------------------------------------------------- /coding-exercise/QUICK_START.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide - Adding New Exercises 2 | 3 | ## 🚀 5-Minute Setup 4 | 5 | ### Step 1: Copy Template (30 seconds) 6 | ```bash 7 | # Copy the template folder 8 | cp -r src/exercises/__template__ src/exercises/exercise-03-your-topic 9 | ``` 10 | 11 | ### Step 2: Edit Files (3 minutes) 12 | 13 | **Edit `Problem.js`:** 14 | - Line 4: Change exercise title 15 | - Line 7-14: Update problem description and options 16 | - Line 19-30: Add your problem code 17 | 18 | **Edit `Solution.js`:** 19 | - Line 4: Change exercise title 20 | - Line 6: Add correct answer 21 | - Line 8-18: Write explanation 22 | - Line 24-80: Add solution code and examples 23 | 24 | ### Step 3: Register Exercise (1 minute) 25 | 26 | **Edit `src/exercises/index.js`:** 27 | 28 | ```javascript 29 | // 1. Import your exercise 30 | import * as Exercise03 from './exercise-03-your-topic'; 31 | 32 | // 2. Add to exercises array 33 | export const exercises = [ 34 | // ... existing exercises 35 | { 36 | id: 'exercise-03', 37 | title: 'Your Topic Name', 38 | description: 'What this exercise teaches', 39 | difficulty: 'Medium', // Easy, Medium, or Hard 40 | topics: ['Hook1', 'Concept2', 'Pattern3'], 41 | Problem: Exercise03.Problem, 42 | Solution: Exercise03.Solution, 43 | }, 44 | ]; 45 | ``` 46 | 47 | ### Step 4: Test (30 seconds) 48 | ```bash 49 | npm start 50 | ``` 51 | 52 | Visit http://localhost:3000 and your exercise should appear! 53 | 54 | ## 📋 Checklist 55 | 56 | - [ ] Copied template folder with correct naming 57 | - [ ] Updated Problem.js with question and code 58 | - [ ] Updated Solution.js with answer and explanation 59 | - [ ] Registered in exercises/index.js 60 | - [ ] Tested in browser 61 | - [ ] No console errors 62 | 63 | ## 🎯 Exercise Naming Convention 64 | 65 | ``` 66 | exercise-[NUMBER]-[topic-name] 67 | 68 | Examples: 69 | ✅ exercise-03-useCallback-optimization 70 | ✅ exercise-04-context-api-pitfalls 71 | ✅ exercise-05-custom-hooks 72 | ❌ Exercise3 73 | ❌ exercise_3_useCallback 74 | ❌ 03-useCallback 75 | ``` 76 | 77 | ## 💡 Quick Tips 78 | 79 | 1. **Keep it focused**: One concept per exercise 80 | 2. **Make it interactive**: Add buttons, inputs, etc. 81 | 3. **Show comparisons**: Wrong vs. Right approach 82 | 4. **Explain thoroughly**: Don't just show code, explain why 83 | 5. **Use console.log**: Help users debug and learn 84 | 85 | ## 🎨 Color Codes (Copy-Paste Ready) 86 | 87 | ```javascript 88 | // Problem/Warning 89 | backgroundColor: '#fff3cd', border: '1px solid #ffc107' 90 | 91 | // Wrong Approach 92 | backgroundColor: '#ffe6e6' 93 | 94 | // Correct Solution 95 | backgroundColor: '#e6ffe6' 96 | 97 | // Information/Tips 98 | backgroundColor: '#e6f3ff' 99 | 100 | // Key Takeaways 101 | backgroundColor: '#f0f0f0' 102 | ``` 103 | 104 | ## 📝 Difficulty Guidelines 105 | 106 | **Easy**: Basic React concepts, straightforward answers 107 | - useState basics 108 | - Props passing 109 | - Simple event handlers 110 | 111 | **Medium**: Intermediate concepts, requires understanding 112 | - State closures 113 | - useEffect dependencies 114 | - Memoization basics 115 | 116 | **Hard**: Advanced patterns, tricky edge cases 117 | - Complex state management 118 | - Performance optimization 119 | - Advanced hooks patterns 120 | 121 | ## 🔍 Topic Suggestions 122 | 123 | Common interview topics: 124 | - useState, useEffect, useRef, useCallback, useMemo 125 | - Closures, State Batching, Event Handlers 126 | - Dependencies, Infinite Loops, Memory Leaks 127 | - Performance, Memoization, Re-renders 128 | - Context API, Custom Hooks, Composition 129 | 130 | ## ⚡ Common Mistakes to Avoid 131 | 132 | ❌ Don't hardcode exercise numbers in multiple places 133 | ✅ Use the exercise ID consistently 134 | 135 | ❌ Don't forget to export in index.js 136 | ✅ Always add both Problem and Solution exports 137 | 138 | ❌ Don't skip the explanation 139 | ✅ Always explain WHY, not just WHAT 140 | 141 | ❌ Don't use external dependencies without updating package.json 142 | ✅ Keep exercises self-contained with inline styles 143 | 144 | ## 🆘 Troubleshooting 145 | 146 | **Exercise doesn't appear on homepage:** 147 | - Check if registered in `exercises/index.js` 148 | - Verify import path is correct 149 | - Ensure index.js exports both Problem and Solution 150 | 151 | **"Exercise not found" error:** 152 | - Check exercise ID matches folder name (without 'exercise-' prefix) 153 | - Verify ID in registry matches navigation string 154 | 155 | **Styling looks broken:** 156 | - Use inline styles for consistency 157 | - Copy styles from existing exercises 158 | - Test in browser dev tools 159 | 160 | ## 📚 Full Documentation 161 | 162 | For detailed information, see [STRUCTURE.md](./STRUCTURE.md) 163 | 164 | --- 165 | 166 | Need help? Check existing exercises for examples! 🎓 167 | -------------------------------------------------------------------------------- /coding-exercise/src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.0/8 are considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl, { 104 | headers: { 'Service-Worker': 'script' }, 105 | }) 106 | .then(response => { 107 | // Ensure service worker exists, and that we really are getting a JS file. 108 | const contentType = response.headers.get('content-type'); 109 | if ( 110 | response.status === 404 || 111 | (contentType != null && contentType.indexOf('javascript') === -1) 112 | ) { 113 | // No service worker found. Probably a different app. Reload the page. 114 | navigator.serviceWorker.ready.then(registration => { 115 | registration.unregister().then(() => { 116 | window.location.reload(); 117 | }); 118 | }); 119 | } else { 120 | // Service worker found. Proceed as normal. 121 | registerValidSW(swUrl, config); 122 | } 123 | }) 124 | .catch(() => { 125 | console.log( 126 | 'No internet connection found. App is running in offline mode.' 127 | ); 128 | }); 129 | } 130 | 131 | export function unregister() { 132 | if ('serviceWorker' in navigator) { 133 | navigator.serviceWorker.ready 134 | .then(registration => { 135 | registration.unregister(); 136 | }) 137 | .catch(error => { 138 | console.error(error.message); 139 | }); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-02-useeffect-dependencies/Solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 2: useEffect Dependencies and Infinite Loops - SOLUTION 5 | * 6 | * ANSWER: B) The count increments infinitely causing an infinite loop 7 | * 8 | * EXPLANATION: 9 | * 10 | * 1. OBJECT REFERENCE PROBLEM: 11 | * - setData({ value: count }) creates a NEW object every time 12 | * - React compares dependencies using Object.is() (similar to ===) 13 | * - { value: 0 } !== { value: 0 } (different references) 14 | * 15 | * 2. THE INFINITE LOOP: 16 | * - Effect runs → creates new data object → triggers re-render 17 | * - Re-render → data dependency changed → effect runs again 18 | * - This continues infinitely! 19 | * 20 | * 3. WHY IT HAPPENS: 21 | * - Objects and arrays are compared by reference, not by value 22 | * - Each render creates a new object reference 23 | * - useEffect sees a "different" dependency and runs again 24 | */ 25 | 26 | function Solution() { 27 | // Example 1: Solution 1 - Remove object from dependencies 28 | const [solution1Count, setSolution1Count] = useState(0); 29 | const [solution1Data, setSolution1Data] = useState({ value: 0 }); 30 | 31 | useEffect(() => { 32 | console.log('Solution 1: Effect runs once on mount'); 33 | setSolution1Count(prev => prev + 1); 34 | setSolution1Data({ value: 0 }); 35 | }, []); // Empty dependency array - runs once on mount 36 | 37 | // Example 2: Solution 2 - Use primitive values in dependencies 38 | const [solution2Count, setSolution2Count] = useState(0); 39 | const [solution2Data, setSolution2Data] = useState({ value: 0 }); 40 | const [trigger, setTrigger] = useState(0); 41 | 42 | useEffect(() => { 43 | console.log('Solution 2: Effect runs when trigger changes'); 44 | setSolution2Count(prev => prev + 1); 45 | setSolution2Data({ value: trigger }); 46 | }, [trigger]); // Depend on primitive value, not object 47 | 48 | // Example 3: Solution 3 - Use useRef for mutable object 49 | const [solution3Count, setSolution3Count] = useState(0); 50 | const solution3DataRef = useRef({ value: 0 }); 51 | 52 | useEffect(() => { 53 | console.log('Solution 3: Effect runs once, uses ref for mutable data'); 54 | setSolution3Count(prev => prev + 1); 55 | solution3DataRef.current = { value: solution3Count }; 56 | }, []); // Refs don't trigger re-renders 57 | 58 | return ( 59 |
60 |

Exercise 2: useEffect Dependencies Solution

61 | 62 |
63 |

❌ The Problem (Code Disabled)

64 |
 65 | {`useEffect(() => {
 66 |   setCount(count + 1);
 67 |   setData({ value: count }); // Creates NEW object
 68 | }, [data]); // Object reference changes every time!
 69 | 
 70 | // Result: INFINITE LOOP 🔄`}
 71 |         
72 |

73 | Why it loops: Each effect run creates a new object, 74 | which triggers the effect again because the reference changed. 75 |

76 |
77 | 78 |
79 |

✅ Solution 1: Empty Dependency Array

80 |

Count: {solution1Count}

81 |

Data Value: {solution1Data.value}

82 |
 83 | {`useEffect(() => {
 84 |   setSolution1Count(prev => prev + 1);
 85 |   setSolution1Data({ value: 0 });
 86 | }, []); // Empty array = runs once on mount`}
 87 |         
88 |

89 | Use when: You only need to run the effect once on component mount 90 |

91 |
92 | 93 |
94 |

✅ Solution 2: Depend on Primitive Values

95 |

Count: {solution2Count}

96 |

Data Value: {solution2Data.value}

97 | 100 |
101 | {`const [trigger, setTrigger] = useState(0);
102 | 
103 | useEffect(() => {
104 |   setSolution2Count(prev => prev + 1);
105 |   setSolution2Data({ value: trigger });
106 | }, [trigger]); // Primitive value - safe to use`}
107 |         
108 |

109 | Use when: You need to react to specific value changes 110 |

111 |
112 | 113 |
114 |

✅ Solution 3: Use useRef for Mutable Data

115 |

Count: {solution3Count}

116 |

Data Value (from ref): {solution3DataRef.current.value}

117 |
118 | {`const dataRef = useRef({ value: 0 });
119 | 
120 | useEffect(() => {
121 |   setSolution3Count(prev => prev + 1);
122 |   dataRef.current = { value: count }; // Mutate ref
123 | }, []); // Refs don't cause re-renders`}
124 |         
125 |

126 | Use when: You need to store mutable data without triggering re-renders 127 |

128 |
129 | 130 |
131 |

📚 Key Takeaways

132 |
    133 |
  1. Objects/Arrays are compared by reference: {`{ } !== { }`} even if contents are the same
  2. 134 |
  3. Avoid objects in dependencies: They create new references on every render
  4. 135 |
  5. Use primitive values: Numbers, strings, booleans are compared by value
  6. 136 |
  7. Empty array []: Effect runs once on mount only
  8. 137 |
  9. No array: Effect runs after every render (usually not what you want)
  10. 138 |
  11. useRef for mutable data: Doesn't trigger re-renders when changed
  12. 139 |
  13. ESLint plugin: Use eslint-plugin-react-hooks to catch these issues
  14. 140 |
141 |
142 | 143 |
144 |

⚠️ Common Mistakes

145 |
    146 |
  • ❌ Putting objects/arrays directly in dependency array
  • 147 |
  • ❌ Creating new objects/arrays inside the component body
  • 148 |
  • ❌ Ignoring ESLint warnings about missing dependencies
  • 149 |
  • ✅ Extract primitive values from objects for dependencies
  • 150 |
  • ✅ Use useMemo/useCallback to stabilize object references
  • 151 |
  • ✅ Consider if you really need the dependency
  • 152 |
153 |
154 |
155 | ); 156 | } 157 | 158 | export default Solution; 159 | -------------------------------------------------------------------------------- /coding-exercise/ARCHITECTURE.md: -------------------------------------------------------------------------------- 1 | # Architecture Overview 2 | 3 | ## 📊 Visual Structure 4 | 5 | ``` 6 | coding-exercise/ 7 | │ 8 | ├── 📄 STRUCTURE.md # Detailed documentation 9 | ├── 📄 QUICK_START.md # Quick guide for adding exercises 10 | ├── 📄 ARCHITECTURE.md # This file - architecture overview 11 | │ 12 | └── src/ 13 | ├── 📱 App.js # Main app with routing 14 | │ └── Uses: HomePage, NavigationButton, exercises registry 15 | │ 16 | ├── 🧩 components/ # Reusable UI components 17 | │ ├── HomePage.js # Landing page 18 | │ ├── ExerciseCard.js # Exercise card component 19 | │ └── NavigationButton.js # Back button 20 | │ 21 | └── 📚 exercises/ # All exercises 22 | ├── index.js # ⭐ EXERCISE REGISTRY (add new exercises here) 23 | │ 24 | ├── __template__/ # Template for new exercises 25 | │ ├── Problem.js 26 | │ ├── Solution.js 27 | │ ├── index.js 28 | │ └── README.md 29 | │ 30 | ├── exercise-01-state-batching/ 31 | │ ├── Problem.js 32 | │ ├── Solution.js 33 | │ └── index.js 34 | │ 35 | └── exercise-02-useeffect-dependencies/ 36 | ├── Problem.js 37 | ├── Solution.js 38 | └── index.js 39 | ``` 40 | 41 | ## 🔄 Data Flow 42 | 43 | ``` 44 | ┌─────────────────────────────────────────────────────────────┐ 45 | │ App.js │ 46 | │ - Manages currentView state │ 47 | │ - Routes between home and exercise views │ 48 | └─────────────────────────────────────────────────────────────┘ 49 | │ 50 | ├─── home ────────────────────┐ 51 | │ │ 52 | │ ▼ 53 | │ ┌──────────────────┐ 54 | │ │ HomePage.js │ 55 | │ │ - Lists all │ 56 | │ │ exercises │ 57 | │ │ - Uses │ 58 | │ │ ExerciseCard │ 59 | │ └──────────────────┘ 60 | │ │ 61 | │ │ reads from 62 | │ ▼ 63 | │ ┌──────────────────┐ 64 | │ │ exercises/ │ 65 | │ │ index.js │ 66 | │ │ (Registry) │ 67 | │ └──────────────────┘ 68 | │ 69 | └─── exercise-XX-problem ─────┐ 70 | └─── exercise-XX-solution ────┤ 71 | │ 72 | ▼ 73 | ┌──────────────────────────┐ 74 | │ Dynamically loads │ 75 | │ Problem or Solution │ 76 | │ component from registry │ 77 | └──────────────────────────┘ 78 | ``` 79 | 80 | ## 🏗️ Component Hierarchy 81 | 82 | ``` 83 | App 84 | ├── NavigationButton (when not on home) 85 | └── Current View 86 | ├── HomePage 87 | │ └── ExerciseCard (for each exercise) 88 | │ ├── View Problem button 89 | │ └── View Solution button 90 | │ 91 | └── Exercise Component (Problem or Solution) 92 | └── Interactive React components 93 | ``` 94 | 95 | ## 🎯 Key Design Decisions 96 | 97 | ### 1. **Centralized Registry Pattern** 98 | - **File**: `exercises/index.js` 99 | - **Why**: Single source of truth for all exercises 100 | - **Benefit**: Add exercises without touching App.js 101 | 102 | ### 2. **Folder-per-Exercise Structure** 103 | - **Pattern**: `exercise-XX-topic-name/` 104 | - **Why**: Self-contained, easy to find and maintain 105 | - **Benefit**: Clear organization as project scales 106 | 107 | ### 3. **Component Reusability** 108 | - **Location**: `components/` 109 | - **Why**: Shared UI components 110 | - **Benefit**: Consistent styling and behavior 111 | 112 | ### 4. **Template-Based Creation** 113 | - **Location**: `exercises/__template__/` 114 | - **Why**: Standardized structure 115 | - **Benefit**: Fast exercise creation, consistent format 116 | 117 | ### 5. **Dynamic Routing** 118 | - **Implementation**: String-based view state 119 | - **Why**: Simple, no external router needed 120 | - **Benefit**: Lightweight, easy to understand 121 | 122 | ## 📦 Module Dependencies 123 | 124 | ``` 125 | App.js 126 | ↓ imports 127 | ├── HomePage (from components/) 128 | ├── NavigationButton (from components/) 129 | └── exercises (from exercises/index.js) 130 | 131 | HomePage.js 132 | ↓ imports 133 | ├── ExerciseCard (from components/) 134 | └── exercises (from exercises/index.js) 135 | 136 | exercises/index.js 137 | ↓ imports 138 | ├── exercise-01-state-batching 139 | ├── exercise-02-useeffect-dependencies 140 | └── ... (more exercises) 141 | 142 | Each exercise folder 143 | ↓ exports via index.js 144 | ├── Problem component 145 | └── Solution component 146 | ``` 147 | 148 | ## 🔑 Key Files 149 | 150 | | File | Purpose | When to Edit | 151 | |------|---------|--------------| 152 | | `exercises/index.js` | Exercise registry | **Every time** you add a new exercise | 153 | | `exercises/__template__/` | Template files | When creating a new exercise (copy it) | 154 | | `components/ExerciseCard.js` | Exercise card UI | To change how exercises are displayed | 155 | | `components/HomePage.js` | Home page layout | To change home page structure | 156 | | `App.js` | Main routing logic | Rarely (architecture is stable) | 157 | 158 | ## 🚀 Scaling Strategy 159 | 160 | ### Adding 10 Exercises 161 | ✅ Current structure handles this perfectly 162 | - Just add folders and register in index.js 163 | 164 | ### Adding 50 Exercises 165 | ✅ Current structure still works 166 | - Consider adding categories in registry 167 | - Group exercises by difficulty or topic 168 | 169 | ### Adding 100+ Exercises 170 | ⚠️ Consider enhancements: 171 | - Add search/filter functionality 172 | - Implement category pages 173 | - Add pagination 174 | - Create difficulty-based navigation 175 | 176 | ## 🎨 Styling Strategy 177 | 178 | **Current**: Inline styles 179 | - ✅ No CSS conflicts 180 | - ✅ Component-scoped 181 | - ✅ Easy to copy/paste 182 | - ✅ Works without build config 183 | 184 | **Future**: If needed 185 | - CSS Modules for shared styles 186 | - Styled Components for complex styling 187 | - Tailwind CSS for utility classes 188 | 189 | ## 🧪 Testing Strategy 190 | 191 | ### Current Setup 192 | - Manual testing in browser 193 | - Visual verification 194 | 195 | ### Recommended Additions 196 | ```javascript 197 | // Example test structure 198 | describe('Exercise 01', () => { 199 | it('renders problem correctly', () => { 200 | // Test problem component 201 | }); 202 | 203 | it('renders solution correctly', () => { 204 | // Test solution component 205 | }); 206 | }); 207 | ``` 208 | 209 | ## 📈 Performance Considerations 210 | 211 | ### Current Optimizations 212 | - ✅ No unnecessary re-renders (simple state management) 213 | - ✅ Lazy loading not needed (small bundle size) 214 | - ✅ Inline styles (no CSS parsing overhead) 215 | 216 | ### Future Optimizations (if needed) 217 | - React.lazy() for code splitting 218 | - useMemo for expensive calculations 219 | - Virtual scrolling for large exercise lists 220 | 221 | ## 🔒 Best Practices 222 | 223 | 1. **One Exercise = One Folder**: Keep exercises self-contained 224 | 2. **Register Immediately**: Add to registry right after creating 225 | 3. **Follow Naming**: Use `exercise-XX-topic-name` format 226 | 4. **Test Before Commit**: Always run `npm start` to verify 227 | 5. **Document Complex Logic**: Add comments for tricky code 228 | 6. **Keep Templates Updated**: Update `__template__` with improvements 229 | 230 | ## 🎓 Learning Path 231 | 232 | For new contributors: 233 | 1. Read `QUICK_START.md` (5 min) 234 | 2. Browse existing exercises (10 min) 235 | 3. Copy template and create test exercise (15 min) 236 | 4. Read `STRUCTURE.md` for details (10 min) 237 | 5. Review this architecture doc (5 min) 238 | 239 | Total: ~45 minutes to full productivity 240 | 241 | ## 🤝 Contributing Guidelines 242 | 243 | 1. **Before adding an exercise**: 244 | - Check if topic already exists 245 | - Ensure it's interview-relevant 246 | - Verify it teaches one clear concept 247 | 248 | 2. **When adding an exercise**: 249 | - Use the template 250 | - Follow naming conventions 251 | - Test thoroughly 252 | - Add clear explanations 253 | 254 | 3. **After adding an exercise**: 255 | - Verify navigation works 256 | - Check for console errors 257 | - Test on different screen sizes 258 | - Update documentation if needed 259 | 260 | --- 261 | 262 | **Questions?** Check the other documentation files or review existing exercises for examples. 263 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-03-useCallback-memoization/Solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback, memo } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 3: useCallback and Unnecessary Re-renders - SOLUTION 5 | * 6 | * ANSWER: B) ChildComponent re-renders every time count changes 7 | * 8 | * EXPLANATION: 9 | * 10 | * 1. FUNCTION REFERENCE PROBLEM: 11 | * - Every time the parent re-renders, handleClick is recreated 12 | * - Even though the function does the same thing, it's a NEW reference 13 | * - React compares props by reference: oldFunction !== newFunction 14 | * 15 | * 2. WHY IT RE-RENDERS: 16 | * - Parent count changes → Parent re-renders 17 | * - Parent re-render → handleClick recreated with new reference 18 | * - New reference → Child sees "different" prop → Child re-renders 19 | * 20 | * 3. THE SOLUTION: 21 | * - Use useCallback to memoize the function 22 | * - Use React.memo to prevent re-renders when props haven't changed 23 | * - Combine both for optimal performance 24 | */ 25 | 26 | // Problem: Regular child component (re-renders on every parent render) 27 | const RegularChild = ({ onClick, label }) => { 28 | console.log(`${label} rendered`); 29 | return ( 30 |
31 |

{label}

32 | 33 |
34 | ); 35 | }; 36 | 37 | // Solution 1: Memoized child (still re-renders because function reference changes) 38 | const MemoizedChild = memo(({ onClick, label }) => { 39 | console.log(`${label} rendered`); 40 | return ( 41 |
42 |

{label}

43 | 44 |
45 | ); 46 | }); 47 | 48 | // Solution 2: Optimized child (won't re-render unnecessarily) 49 | const OptimizedChild = memo(({ onClick, label }) => { 50 | console.log(`${label} rendered`); 51 | return ( 52 |
53 |

{label}

54 | 55 |
56 | ); 57 | }); 58 | 59 | function Solution() { 60 | const [count, setCount] = useState(0); 61 | const [clicks1, setClicks1] = useState(0); 62 | const [clicks2, setClicks2] = useState(0); 63 | const [clicks3, setClicks3] = useState(0); 64 | 65 | // ❌ WRONG: Function recreated on every render 66 | const handleClick1 = () => { 67 | setClicks1(prev => prev + 1); 68 | }; 69 | 70 | // ⚠️ PARTIAL: Function recreated on every render (memo doesn't help) 71 | const handleClick2 = () => { 72 | setClicks2(prev => prev + 1); 73 | }; 74 | 75 | // ✅ CORRECT: Function memoized with useCallback 76 | const handleClick3 = useCallback(() => { 77 | setClicks3(prev => prev + 1); 78 | }, []); // Empty deps = function never changes 79 | 80 | return ( 81 |
82 |

Exercise 3: useCallback & Memoization Solution

83 | 84 |
90 |

Parent State

91 |

Parent Count: {count}

92 | 95 |
96 | 97 | {/* Example 1: Regular child without memo */} 98 |
99 |

❌ Problem: Regular Child (No Optimization)

100 |

Clicks: {clicks1}

101 | 105 |
112 | {`const handleClick = () => {
113 |   setClicks(prev => prev + 1);
114 | };
115 | 
116 | 
117 | 
118 | // Problem: Function recreated every render
119 | // Child re-renders every time`}
120 |         
121 |
122 | 123 | {/* Example 2: Memoized child but function still recreated */} 124 |
125 |

⚠️ Partial: Memo Child (Still Re-renders)

126 |

Clicks: {clicks2}

127 | 131 |
138 | {`const MemoizedChild = memo(({ onClick }) => {
139 |   return ;
140 | });
141 | 
142 | const handleClick = () => { /* ... */ };
143 | 
144 | 
145 | // Problem: memo() helps, but function still
146 | // recreated, so props still "change"`}
147 |         
148 |
149 | 150 | {/* Example 3: Optimized with useCallback + memo */} 151 |
152 |

✅ Solution: useCallback + memo

153 |

Clicks: {clicks3}

154 | 158 |
165 | {`const OptimizedChild = memo(({ onClick }) => {
166 |   return ;
167 | });
168 | 
169 | const handleClick = useCallback(() => {
170 |   setClicks(prev => prev + 1);
171 | }, []); // Memoized - same reference
172 | 
173 | 
174 | 
175 | // ✅ Function reference stays the same
176 | // ✅ memo() prevents re-render
177 | // ✅ Only re-renders when actually needed`}
178 |         
179 |
180 | 181 | {/* Key Takeaways */} 182 |
188 |

📚 Key Takeaways

189 |
    190 |
  1. Functions are recreated: On every render, function declarations create new references
  2. 191 |
  3. Reference equality: React compares props using === (reference comparison)
  4. 192 |
  5. useCallback: Memoizes function references between renders
  6. 193 |
  7. React.memo: Prevents re-renders when props haven't changed
  8. 194 |
  9. Combine both: useCallback + memo for optimal performance
  10. 195 |
  11. Dependencies matter: useCallback deps determine when function updates
  12. 196 |
197 |
198 | 199 | {/* When to Use */} 200 |
206 |

💡 When to Use useCallback

207 |
    208 |
  • ✅ Passing callbacks to memoized child components
  • 209 |
  • ✅ Function is a dependency in useEffect/useMemo
  • 210 |
  • ✅ Expensive child components that re-render often
  • 211 |
  • ❌ Simple components without performance issues
  • 212 |
  • ❌ Functions that change on every render anyway
  • 213 |
  • ❌ Premature optimization (measure first!)
  • 214 |
215 |
216 | 217 | {/* Common Mistakes */} 218 |
224 |

⚠️ Common Mistakes

225 |
    226 |
  • ❌ Using useCallback without React.memo on child
  • 227 |
  • ❌ Forgetting dependencies (causes stale closures)
  • 228 |
  • ❌ Over-optimizing everything (adds complexity)
  • 229 |
  • ❌ Using useCallback for functions that change often
  • 230 |
  • ✅ Profile first, optimize when needed
  • 231 |
  • ✅ Use React DevTools Profiler to find bottlenecks
  • 232 |
233 |
234 | 235 | {/* Code Comparison */} 236 |
242 |

📊 Performance Comparison

243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
ApproachRe-rendersPerformance
Regular Child❌ Every parent renderPoor
memo() only❌ Every parent renderPoor
useCallback + memo✅ Only when neededExcellent
269 |
270 |
271 | ); 272 | } 273 | 274 | export default Solution; 275 | -------------------------------------------------------------------------------- /coding-exercise/src/exercises/exercise-04-custom-hooks/Solution.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | /** 4 | * CODING EXERCISE 4: Custom Hooks and Code Reusability - SOLUTION 5 | * 6 | * ANSWER: B) Code duplication - should extract into a custom hook 7 | * 8 | * EXPLANATION: 9 | * 10 | * 1. THE PROBLEM: 11 | * - Both components have identical data fetching logic 12 | * - Duplicate state management (data, loading, error) 13 | * - Hard to maintain - changes need to be made in multiple places 14 | * 15 | * 2. THE SOLUTION: 16 | * - Extract common logic into a custom hook 17 | * - Custom hooks start with "use" prefix 18 | * - Can use other hooks inside (useState, useEffect, etc.) 19 | * - Returns values that components need 20 | * 21 | * 3. BENEFITS: 22 | * - DRY (Don't Repeat Yourself) 23 | * - Easier to test 24 | * - Easier to maintain 25 | * - Reusable across components 26 | */ 27 | 28 | // ✅ SOLUTION: Custom Hook 29 | function useFetch(fetchFunction, dependencies = []) { 30 | const [data, setData] = useState(null); 31 | const [loading, setLoading] = useState(true); 32 | const [error, setError] = useState(null); 33 | 34 | useEffect(() => { 35 | let cancelled = false; 36 | 37 | const fetchData = async () => { 38 | setLoading(true); 39 | setError(null); 40 | 41 | try { 42 | const result = await fetchFunction(); 43 | if (!cancelled) { 44 | setData(result); 45 | setLoading(false); 46 | } 47 | } catch (err) { 48 | if (!cancelled) { 49 | setError(err.message); 50 | setLoading(false); 51 | } 52 | } 53 | }; 54 | 55 | fetchData(); 56 | 57 | // Cleanup function to prevent state updates on unmounted component 58 | return () => { 59 | cancelled = true; 60 | }; 61 | }, dependencies); // eslint-disable-line react-hooks/exhaustive-deps 62 | 63 | return { data, loading, error }; 64 | } 65 | 66 | // Simulated API functions 67 | const fetchUser = () => { 68 | return new Promise((resolve) => { 69 | setTimeout(() => { 70 | resolve({ name: 'John Doe', email: 'john@example.com', role: 'Developer' }); 71 | }, 1000); 72 | }); 73 | }; 74 | 75 | const fetchPosts = () => { 76 | return new Promise((resolve) => { 77 | setTimeout(() => { 78 | resolve([ 79 | { id: 1, title: 'Understanding React Hooks', likes: 42 }, 80 | { id: 2, title: 'Custom Hooks Best Practices', likes: 38 }, 81 | { id: 3, title: 'Building Reusable Components', likes: 55 } 82 | ]); 83 | }, 1200); 84 | }); 85 | }; 86 | 87 | const fetchComments = () => { 88 | return new Promise((resolve) => { 89 | setTimeout(() => { 90 | resolve([ 91 | { id: 1, text: 'Great article!', author: 'Alice' }, 92 | { id: 2, text: 'Very helpful, thanks!', author: 'Bob' } 93 | ]); 94 | }, 800); 95 | }); 96 | }; 97 | 98 | // ✅ Component 1: Using custom hook 99 | function UserProfile() { 100 | const { data, loading, error } = useFetch(fetchUser, []); 101 | 102 | if (loading) return
Loading user...
; 103 | if (error) return
Error: {error}
; 104 | 105 | return ( 106 |
107 |

👤 User Profile

108 |

Name: {data?.name}

109 |

Email: {data?.email}

110 |

Role: {data?.role}

111 |
112 | ); 113 | } 114 | 115 | // ✅ Component 2: Using custom hook 116 | function PostsList() { 117 | const { data, loading, error } = useFetch(fetchPosts, []); 118 | 119 | if (loading) return
Loading posts...
; 120 | if (error) return
Error: {error}
; 121 | 122 | return ( 123 |
124 |

📝 Posts

125 | {data?.map(post => ( 126 |
127 | {post.title} - {post.likes} likes 128 |
129 | ))} 130 |
131 | ); 132 | } 133 | 134 | // ✅ Component 3: Using custom hook (easy to add new features!) 135 | function CommentsList() { 136 | const { data, loading, error } = useFetch(fetchComments, []); 137 | 138 | if (loading) return
Loading comments...
; 139 | if (error) return
Error: {error}
; 140 | 141 | return ( 142 |
143 |

💬 Comments

144 | {data?.map(comment => ( 145 |
146 | "{comment.text}" - {comment.author} 147 |
148 | ))} 149 |
150 | ); 151 | } 152 | 153 | function Solution() { 154 | return ( 155 |
156 |

Exercise 4: Custom Hooks Solution

157 | 158 | {/* The Solution */} 159 |
165 |

✅ Solution: useFetch Custom Hook

166 |
173 | {`function useFetch(fetchFunction, dependencies = []) {
174 |   const [data, setData] = useState(null);
175 |   const [loading, setLoading] = useState(true);
176 |   const [error, setError] = useState(null);
177 | 
178 |   useEffect(() => {
179 |     let cancelled = false;
180 | 
181 |     const fetchData = async () => {
182 |       setLoading(true);
183 |       setError(null);
184 |       
185 |       try {
186 |         const result = await fetchFunction();
187 |         if (!cancelled) {
188 |           setData(result);
189 |           setLoading(false);
190 |         }
191 |       } catch (err) {
192 |         if (!cancelled) {
193 |           setError(err.message);
194 |           setLoading(false);
195 |         }
196 |       }
197 |     };
198 | 
199 |     fetchData();
200 | 
201 |     return () => { cancelled = true; };
202 |   }, dependencies);
203 | 
204 |   return { data, loading, error };
205 | }`}
206 |         
207 |
208 | 209 | {/* Live Examples */} 210 |
211 |

🎯 Live Examples Using Custom Hook

212 | 213 | 214 | 215 |
216 | 217 | {/* Usage Example */} 218 |
224 |

💡 How to Use

225 |
232 | {`// Define your fetch function
233 | const fetchUser = () => {
234 |   return fetch('/api/user').then(res => res.json());
235 | };
236 | 
237 | // Use the custom hook in your component
238 | function UserProfile() {
239 |   const { data, loading, error } = useFetch(fetchUser, []);
240 | 
241 |   if (loading) return 
Loading...
; 242 | if (error) return
Error: {error}
; 243 | return
{data.name}
; 244 | }`} 245 |
246 |
247 | 248 | {/* Key Takeaways */} 249 |
255 |

📚 Key Takeaways

256 |
    257 |
  1. Custom hooks extract reusable logic: Share stateful logic between components
  2. 258 |
  3. Naming convention: Always start with "use" prefix (e.g., useFetch, useForm)
  4. 259 |
  5. Can use other hooks: useState, useEffect, useContext, etc.
  6. 260 |
  7. Return what components need: Usually an object or array of values
  8. 261 |
  9. Cleanup is important: Prevent memory leaks with cleanup functions
  10. 262 |
  11. Dependencies matter: Pass dependencies to control when hook re-runs
  12. 263 |
  13. Testable: Custom hooks can be tested independently
  14. 264 |
265 |
266 | 267 | {/* Before vs After */} 268 |
274 |

📊 Before vs After Comparison

275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 |
AspectBefore (Duplicated)After (Custom Hook)
Code Lines~30 lines per component~5 lines per component
Maintainability❌ Update in multiple places✅ Update in one place
Testability❌ Test each component✅ Test hook once
Reusability❌ Copy-paste code✅ Import and use
Bug Fixes❌ Fix in all places✅ Fix once
311 |
312 | 313 | {/* Common Custom Hooks */} 314 |
320 |

🔧 Common Custom Hook Patterns

321 |
    322 |
  • useFetch: Data fetching with loading/error states
  • 323 |
  • useForm: Form state management and validation
  • 324 |
  • useLocalStorage: Sync state with localStorage
  • 325 |
  • useDebounce: Debounce values (e.g., search input)
  • 326 |
  • useWindowSize: Track window dimensions
  • 327 |
  • useOnClickOutside: Detect clicks outside element
  • 328 |
  • usePrevious: Store previous value of a prop/state
  • 329 |
  • useToggle: Boolean state with toggle function
  • 330 |
331 |
332 | 333 | {/* Best Practices */} 334 |
339 |

✨ Custom Hook Best Practices

340 |
    341 |
  • Start with "use": Required for React to recognize it as a hook
  • 342 |
  • Keep it focused: One responsibility per hook
  • 343 |
  • Return objects for flexibility: Easy to add new values later
  • 344 |
  • Add cleanup: Always clean up side effects
  • 345 |
  • Document parameters: Make it clear what the hook expects
  • 346 |
  • Handle edge cases: Loading, errors, empty states
  • 347 |
  • Don't call conditionally: Hooks must be called in the same order
  • 348 |
  • Don't call in loops: Violates Rules of Hooks
  • 349 |
350 |
351 |
352 | ); 353 | } 354 | 355 | export default Solution; 356 | -------------------------------------------------------------------------------- /coding-exercise/README.md: -------------------------------------------------------------------------------- 1 | ### Coding Exercise 2 | 3 | #### 1. What is the output of below code 4 | 5 | ```javascript 6 | import { useState } from 'react'; 7 | 8 | export default function Counter() { 9 | const [counter, setCounter] = useState(5); 10 | 11 | return ( 12 | <> 13 | {counter} 14 | 21 | 22 | ) 23 | } 24 | ``` 25 | 26 | - 1: Alert with 5, 5 27 | - 2: Alert with 15, 25 28 | - 3: Alert with 5, 10 29 | - 4: Error: Cannot update the same state multiple times concurrently 30 | 31 |
Answer 32 |

33 | 34 | ##### Answer: 3 35 | State values are fixed(i.e, default value 5) in each render and setting the state only changes it for the next render. React will wait until all the code executed with in an event handler before your state updates followed by re-rendering the UI. Also, all the 3 setter function calls are replacing the calculated value. Hence, irrespective of how many times you call `setCounter(counter + 5)` the final value is 10(5+5). 36 | 37 | This can be visuallized by substituting with state variable values in the particular render, 38 | ```javascript 39 | 46 | ``` 47 |

48 |
49 | 50 | --- 51 | 52 | **[⬆ Back to Top](#table-of-contents)** 53 | 54 | #### 2. What is the output of below code 55 | 56 | ```javascript 57 | import { useState } from 'react'; 58 | 59 | export default function Counter() { 60 | const [counter, setCounter] = useState(5); 61 | 62 | return ( 63 | <> 64 | {counter} 65 | 72 | 73 | ) 74 | } 75 | ``` 76 | 77 | - 1: Alert with 5, 25 78 | - 2: Alert with 5, 10 79 | - 3: Alert with 15, 25 80 | - 4: Alert with 15, 10 81 | 82 |
Answer 83 |

84 | 85 | ##### Answer: 1 86 | 87 | React queues all the updater functions(e.g, counter => counter + 5) which will be processed after all the code inside event handler has been executed. During the next re-render(state update through setState), React goes through the queue and increment the counter based on the previous value in each function call. So the final value of counter becomes 25(initial value 5 + 5 + 5 + 5 + 5) whereas the alert shows default value 5 because the counter value won't be updated by that time. 88 | 89 |

90 |
91 | 92 | --- 93 | 94 | **[⬆ Back to Top](#table-of-contents)** 95 | 96 | #### 3. What is the output of span after one click? 97 | 98 | ```javascript 99 | import { useRef } from 'react'; 100 | 101 | export default function Counter() { 102 | let countRef = useRef(0); 103 | 104 | function handleIncrement() { 105 | countRef.current = countRef.current + 1; 106 | } 107 | 108 | return ( 109 | <> 110 | Count: {countRef.current} 111 | 114 | 115 | ) 116 | } 117 | ``` 118 | 119 | - 1: Cannot read current property of undefined 120 | - 2: Count: 1 121 | - 3: null 122 | - 4: Count: 0 123 | 124 |
Answer 125 |

126 | 127 | ##### Answer: 4 128 | 129 | In React, every update has two phases. 130 | 1. **Render:** This is where React calls the components in order to output something on the screen 131 | 2. **Commit:** React applies changes to the DOM 132 | 133 | Any updates to the ref will be reflected only in the commit phase. In other words, React sets **counterRef.current** during the commit phase. Hence, **countRef.current** always holds value `0` irrespective of how many times the Increment button clicked. 134 |

135 |
136 | 137 | --- 138 | 139 | **[⬆ Back to Top](#table-of-contents)** 140 | 141 | #### 4. What is the outcome of below code after button click? 142 | 143 | ```javascript 144 | import { useRef } from 'react'; 145 | 146 | function MyCustomInput(props) { 147 | return ; 148 | } 149 | 150 | export default function MyCustomForm() { 151 | const inputRef = useRef(null); 152 | 153 | function handleInputFocus() { 154 | inputRef.current.focus(); 155 | } 156 | 157 | return ( 158 | <> 159 | 160 | 163 | 164 | ); 165 | } 166 | ``` 167 | 168 | - 1: Input gets the focus 169 | - 2: Warning: Function components cannot be given refs. 170 | - 3: Cannot read current property of undefined 171 | - 4: Warning: Missing ref on element 172 | 173 |
Answer 174 |

175 | 176 | ##### Answer: 2 177 | By default, React does not allow a component access the DOM nodes of other components even for child components. If you still try to access the DOM nodes directly then you will receive below error: 178 | 179 | ```javascript 180 | Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()? 181 | ``` 182 | 183 | This issue can be fixed by wrapping the **** component with `forwardRef` function which accepts ref as the second argument which can be used on the **** element as **ref={ref}** 184 | 185 |

186 |
187 | 188 | --- 189 | 190 | **[⬆ Back to Top](#table-of-contents)** 191 | 192 | #### 5. What is the outcome of number of clicks after 3 button clicks? 193 | 194 | ```javascript 195 | import { useRef } from 'react'; 196 | 197 | export default function Counter() { 198 | let ref = useRef(0); 199 | 200 | function handleClick() { 201 | ref.current = ref.current + 1; 202 | } 203 | 204 | return ( 205 | <> 206 |
Clicked {ref.current} times
207 | 210 | 211 | ); 212 | } 213 | ``` 214 | 215 | - 1: 3 times 216 | - 2: 4 times 217 | - 3: 2 times 218 | - 4: 0 times 219 | 220 |
Answer 221 |

222 | 223 | ##### Answer: 4 224 | If you try to use **{ref.current}** in the render method, the number won’t be updated on click. This is because **ref.current** does not trigger a re-render unlike state. This property is mainly used to read and write the values inside event handler or outside the render method. 225 |

226 |
227 | 228 | --- 229 | 230 | **[⬆ Back to Top](#table-of-contents)** 231 | 232 | #### 6. What is the output in the console after mounting and unmounting the component? 233 | 234 | ```javascript 235 | import { useEffect, useState } from 'react'; 236 | 237 | export default function Timer() { 238 | const [count, setCount] = useState(0); 239 | 240 | useEffect(() => { 241 | console.log('Effect ran'); 242 | const timer = setInterval(() => { 243 | setCount(c => c + 1); 244 | }, 1000); 245 | 246 | console.log('Cleanup registered'); 247 | return () => { 248 | console.log('Cleanup executed'); 249 | clearInterval(timer); 250 | }; 251 | }, []); 252 | 253 | return
Count: {count}
; 254 | } 255 | ``` 256 | 257 | - 1: "Effect ran", "Cleanup registered", "Cleanup executed" (on mount), "Cleanup executed" (on unmount) 258 | - 2: "Effect ran", "Cleanup registered" (on mount), "Cleanup executed" (on unmount) 259 | - 3: "Effect ran" (on mount), "Cleanup executed" (on unmount) 260 | - 4: "Effect ran", "Cleanup registered", "Cleanup executed" (on mount and unmount) 261 | 262 |
Answer 263 |

264 | 265 | ##### Answer: 2 266 | 267 | When a component mounts, React runs the effect function completely, which logs both "Effect ran" and "Cleanup registered". The cleanup function (returned from useEffect) is **not** executed during mount - it's only registered at this point. 268 | 269 | The cleanup function executes in two scenarios: 270 | 1. **Before re-running the effect** (if dependencies change) 271 | 2. **When the component unmounts** 272 | 273 | Since the dependency array is empty `[]`, the effect only runs once on mount and never re-runs. Therefore, the cleanup function will only execute when the component unmounts, logging "Cleanup executed" and clearing the interval timer. 274 | 275 | This pattern is crucial for preventing memory leaks when using timers, subscriptions, or event listeners in React components. 276 | 277 |

278 |
279 | 280 | --- 281 | 282 | **[⬆ Back to Top](#table-of-contents)** 283 | 284 | #### 7. What will be the output after clicking the button? 285 | 286 | ```javascript 287 | import { useState } from 'react'; 288 | 289 | export default function App() { 290 | const [items, setItems] = useState([1, 2, 3]); 291 | 292 | function handleClick() { 293 | items.push(4); 294 | setItems(items); 295 | } 296 | 297 | return ( 298 | <> 299 |
Items: {items.join(', ')}
300 | 301 | 302 | ); 303 | } 304 | ``` 305 | 306 | - 1: Items: 1, 2, 3, 4 307 | - 2: Items: 1, 2, 3 308 | - 3: Error: Cannot mutate state directly 309 | - 4: Items: 1, 2, 3, 4, 4 (duplicates on each click) 310 | 311 |
Answer 312 |

313 | 314 | ##### Answer: 2 315 | 316 | React uses `Object.is()` comparison to detect state changes. When you mutate the array directly using `items.push(4)` and then pass the same array reference to `setItems(items)`, React sees it as the same object (same reference) and doesn't trigger a re-render. 317 | 318 | To properly update an array in state, you should create a new array: 319 | ```javascript 320 | setItems([...items, 4]); // or 321 | setItems(items.concat(4)); 322 | ``` 323 | 324 | This is a common mistake that leads to bugs where the UI doesn't update even though the underlying data has changed. Always treat state as immutable in React. 325 | 326 |

327 |
328 | 329 | --- 330 | 331 | **[⬆ Back to Top](#table-of-contents)** 332 | 333 | #### 8. What is the output after the component mounts? 334 | 335 | ```javascript 336 | import { useState, useEffect } from 'react'; 337 | 338 | export default function Counter() { 339 | const [count, setCount] = useState(0); 340 | 341 | useEffect(() => { 342 | setCount(1); 343 | }); 344 | 345 | useEffect(() => { 346 | setCount(2); 347 | }, []); 348 | 349 | console.log('Rendered with count:', count); 350 | 351 | return
Count: {count}
; 352 | } 353 | ``` 354 | 355 | - 1: "Rendered with count: 0", "Rendered with count: 2" 356 | - 2: "Rendered with count: 0", "Rendered with count: 1", "Rendered with count: 2" 357 | - 3: Infinite loop / Maximum update depth exceeded error 358 | - 4: "Rendered with count: 0", "Rendered with count: 2", "Rendered with count: 1" 359 | 360 |
Answer 361 |

362 | 363 | ##### Answer: 3 364 | 365 | This code creates an **infinite loop** because the first `useEffect` has no dependency array, which means it runs after **every render**. Here's what happens: 366 | 367 | 1. Initial render with `count: 0` 368 | 2. After render, both effects run (first sets count to 1, second sets count to 2) 369 | 3. State update triggers re-render with `count: 2` 370 | 4. After render, the first effect runs again (no dependencies), setting count to 1 371 | 5. This triggers another re-render, and the cycle continues infinitely 372 | 373 | React will detect this and throw an error: **"Maximum update depth exceeded"**. 374 | 375 | The fix is to add a dependency array to the first useEffect: 376 | ```javascript 377 | useEffect(() => { 378 | setCount(1); 379 | }, []); // Add empty dependency array 380 | ``` 381 | 382 |

383 |
384 | 385 | --- 386 | 387 | **[⬆ Back to Top](#table-of-contents)** 388 | 389 | #### 9. What will be displayed on the screen? 390 | 391 | ```javascript 392 | import { useMemo } from 'react'; 393 | 394 | export default function App() { 395 | const expensiveCalculation = useMemo(() => { 396 | console.log('Calculating...'); 397 | return 100 + 200; 398 | }); 399 | 400 | return
Result: {expensiveCalculation}
; 401 | } 402 | ``` 403 | 404 | - 1: Result: 300 405 | - 2: Result: function() { ... } 406 | - 3: Result: undefined 407 | - 4: Error: useMemo requires a dependency array 408 | 409 |
Answer 410 |

411 | 412 | ##### Answer: 4 413 | 414 | The `useMemo` hook **requires** a dependency array as the second argument. Without it, React will throw an error during development: 415 | 416 | ``` 417 | Error: useMemo requires a dependency array as the second argument 418 | ``` 419 | 420 | The correct usage is: 421 | ```javascript 422 | const expensiveCalculation = useMemo(() => { 423 | console.log('Calculating...'); 424 | return 100 + 200; 425 | }, []); // Dependency array is required 426 | ``` 427 | 428 | With an empty dependency array `[]`, the calculation runs once during the initial render and the memoized value (300) is reused on subsequent renders. If you include dependencies, the calculation re-runs whenever those dependencies change. 429 | 430 |

431 |
432 | 433 | --- 434 | 435 | **[⬆ Back to Top](#table-of-contents)** 436 | 437 | #### 10. What is the output after clicking the button twice? 438 | 439 | ```javascript 440 | import { useState } from 'react'; 441 | 442 | export default function Form() { 443 | const [person, setPerson] = useState({ 444 | name: 'John', 445 | age: 30 446 | }); 447 | 448 | function handleClick() { 449 | person.age = person.age + 1; 450 | setPerson(person); 451 | } 452 | 453 | return ( 454 | <> 455 |
Age: {person.age}
456 | 457 | 458 | ); 459 | } 460 | ``` 461 | 462 | - 1: Age: 32 463 | - 2: Age: 31 464 | - 3: Age: 30 465 | - 4: Error: Cannot mutate state 466 | 467 |
Answer 468 |

469 | 470 | ##### Answer: 3 471 | 472 | Similar to the array mutation issue, directly mutating an object's property and then passing the same object reference to the state setter doesn't trigger a re-render. React uses `Object.is()` to compare the old and new state values. Since `person` is still the same object reference, React doesn't detect any change. 473 | 474 | Even though `person.age` is being incremented internally, the UI shows the initial value (30) because React never re-renders the component. 475 | 476 | The correct approach is to create a new object: 477 | ```javascript 478 | function handleClick() { 479 | setPerson({ 480 | ...person, 481 | age: person.age + 1 482 | }); 483 | } 484 | ``` 485 | 486 | This creates a new object with a new reference, which React recognizes as a state change and triggers a re-render. This is a fundamental principle in React: **always treat state as immutable**. 487 | 488 |

489 |
490 | 491 | --- 492 | 493 | **[⬆ Back to Top](#table-of-contents)** 494 | 495 | #### 11. What will be logged to the console? 496 | 497 | ```javascript 498 | import { useEffect, useState } from 'react'; 499 | 500 | export default function App() { 501 | const [count, setCount] = useState(0); 502 | 503 | useEffect(() => { 504 | console.log('Effect A'); 505 | return () => console.log('Cleanup A'); 506 | }, [count]); 507 | 508 | useEffect(() => { 509 | console.log('Effect B'); 510 | return () => console.log('Cleanup B'); 511 | }, []); 512 | 513 | console.log('Render'); 514 | 515 | return ; 516 | } 517 | ``` 518 | 519 | **On initial mount:** 520 | - 1: "Render", "Effect A", "Effect B" 521 | - 2: "Effect A", "Effect B", "Render" 522 | - 3: "Render", "Effect B", "Effect A" 523 | - 4: "Effect A", "Cleanup A", "Effect B", "Render" 524 | 525 | **After first button click:** 526 | - 1: "Render", "Cleanup A", "Effect A" 527 | - 2: "Cleanup A", "Render", "Effect A" 528 | - 3: "Render", "Effect A", "Cleanup A" 529 | - 4: "Render", "Cleanup B", "Effect B", "Cleanup A", "Effect A" 530 | 531 |
Answer 532 |

533 | 534 | ##### Answer for initial mount: 1, Answer for first click: 1 535 | 536 | **On initial mount:** 537 | 1. Component renders first: "Render" 538 | 2. After render, effects run in the order they're defined: "Effect A", "Effect B" 539 | 3. Cleanup functions are registered but not executed 540 | 541 | **After first button click:** 542 | 1. State changes, component re-renders: "Render" 543 | 2. Before running Effect A again (because `count` dependency changed), its cleanup runs: "Cleanup A" 544 | 3. Effect A runs again: "Effect A" 545 | 4. Effect B doesn't run because its dependency array `[]` is empty (no changes) 546 | 5. Cleanup B is not called because Effect B doesn't re-run 547 | 548 | **Key takeaways:** 549 | - Render happens first, then effects run 550 | - Effects run in the order they're defined 551 | - Cleanup runs before re-running an effect (when dependencies change) 552 | - Effects with empty dependency arrays only run once on mount 553 | 554 |

555 |
556 | 557 | --- 558 | 559 | **[⬆ Back to Top](#table-of-contents)** 560 | --------------------------------------------------------------------------------