Skip to content

Commit 6a178c8

Browse files
committed
feat: added some coding exercise challenges
1 parent 3c91f19 commit 6a178c8

File tree

1 file changed

+329
-0
lines changed

1 file changed

+329
-0
lines changed

coding-exercise/README.md

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,3 +227,332 @@ If you try to use **{ref.current}** in the render method, the number won’t be
227227
---
228228
229229
**[⬆ Back to Top](#table-of-contents)**
230+
231+
#### 6. What is the output in the console after mounting and unmounting the component?
232+
233+
```javascript
234+
import { useEffect, useState } from 'react';
235+
236+
export default function Timer() {
237+
const [count, setCount] = useState(0);
238+
239+
useEffect(() => {
240+
console.log('Effect ran');
241+
const timer = setInterval(() => {
242+
setCount(c => c + 1);
243+
}, 1000);
244+
245+
console.log('Cleanup registered');
246+
return () => {
247+
console.log('Cleanup executed');
248+
clearInterval(timer);
249+
};
250+
}, []);
251+
252+
return <div>Count: {count}</div>;
253+
}
254+
```
255+
256+
- 1: "Effect ran", "Cleanup registered", "Cleanup executed" (on mount), "Cleanup executed" (on unmount)
257+
- 2: "Effect ran", "Cleanup registered" (on mount), "Cleanup executed" (on unmount)
258+
- 3: "Effect ran" (on mount), "Cleanup executed" (on unmount)
259+
- 4: "Effect ran", "Cleanup registered", "Cleanup executed" (on mount and unmount)
260+
261+
<details><summary><b>Answer</b></summary>
262+
<p>
263+
264+
##### Answer: 2
265+
266+
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.
267+
268+
The cleanup function executes in two scenarios:
269+
1. **Before re-running the effect** (if dependencies change)
270+
2. **When the component unmounts**
271+
272+
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.
273+
274+
This pattern is crucial for preventing memory leaks when using timers, subscriptions, or event listeners in React components.
275+
276+
</p>
277+
</details>
278+
279+
---
280+
281+
**[⬆ Back to Top](#table-of-contents)**
282+
283+
#### 7. What will be the output after clicking the button?
284+
285+
```javascript
286+
import { useState } from 'react';
287+
288+
export default function App() {
289+
const [items, setItems] = useState([1, 2, 3]);
290+
291+
function handleClick() {
292+
items.push(4);
293+
setItems(items);
294+
}
295+
296+
return (
297+
<>
298+
<div>Items: {items.join(', ')}</div>
299+
<button onClick={handleClick}>Add Item</button>
300+
</>
301+
);
302+
}
303+
```
304+
305+
- 1: Items: 1, 2, 3, 4
306+
- 2: Items: 1, 2, 3
307+
- 3: Error: Cannot mutate state directly
308+
- 4: Items: 1, 2, 3, 4, 4 (duplicates on each click)
309+
310+
<details><summary><b>Answer</b></summary>
311+
<p>
312+
313+
##### Answer: 2
314+
315+
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.
316+
317+
To properly update an array in state, you should create a new array:
318+
```javascript
319+
setItems([...items, 4]); // or
320+
setItems(items.concat(4));
321+
```
322+
323+
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.
324+
325+
</p>
326+
</details>
327+
328+
---
329+
330+
**[⬆ Back to Top](#table-of-contents)**
331+
332+
#### 8. What is the output after the component mounts?
333+
334+
```javascript
335+
import { useState, useEffect } from 'react';
336+
337+
export default function Counter() {
338+
const [count, setCount] = useState(0);
339+
340+
useEffect(() => {
341+
setCount(1);
342+
});
343+
344+
useEffect(() => {
345+
setCount(2);
346+
}, []);
347+
348+
console.log('Rendered with count:', count);
349+
350+
return <div>Count: {count}</div>;
351+
}
352+
```
353+
354+
- 1: "Rendered with count: 0", "Rendered with count: 2"
355+
- 2: "Rendered with count: 0", "Rendered with count: 1", "Rendered with count: 2"
356+
- 3: Infinite loop / Maximum update depth exceeded error
357+
- 4: "Rendered with count: 0", "Rendered with count: 2", "Rendered with count: 1"
358+
359+
<details><summary><b>Answer</b></summary>
360+
<p>
361+
362+
##### Answer: 3
363+
364+
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:
365+
366+
1. Initial render with `count: 0`
367+
2. After render, both effects run (first sets count to 1, second sets count to 2)
368+
3. State update triggers re-render with `count: 2`
369+
4. After render, the first effect runs again (no dependencies), setting count to 1
370+
5. This triggers another re-render, and the cycle continues infinitely
371+
372+
React will detect this and throw an error: **"Maximum update depth exceeded"**.
373+
374+
The fix is to add a dependency array to the first useEffect:
375+
```javascript
376+
useEffect(() => {
377+
setCount(1);
378+
}, []); // Add empty dependency array
379+
```
380+
381+
</p>
382+
</details>
383+
384+
---
385+
386+
**[⬆ Back to Top](#table-of-contents)**
387+
388+
#### 9. What will be displayed on the screen?
389+
390+
```javascript
391+
import { useMemo } from 'react';
392+
393+
export default function App() {
394+
const expensiveCalculation = useMemo(() => {
395+
console.log('Calculating...');
396+
return 100 + 200;
397+
});
398+
399+
return <div>Result: {expensiveCalculation}</div>;
400+
}
401+
```
402+
403+
- 1: Result: 300
404+
- 2: Result: function() { ... }
405+
- 3: Result: undefined
406+
- 4: Error: useMemo requires a dependency array
407+
408+
<details><summary><b>Answer</b></summary>
409+
<p>
410+
411+
##### Answer: 4
412+
413+
The `useMemo` hook **requires** a dependency array as the second argument. Without it, React will throw an error during development:
414+
415+
```
416+
Error: useMemo requires a dependency array as the second argument
417+
```
418+
419+
The correct usage is:
420+
```javascript
421+
const expensiveCalculation = useMemo(() => {
422+
console.log('Calculating...');
423+
return 100 + 200;
424+
}, []); // Dependency array is required
425+
```
426+
427+
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.
428+
429+
</p>
430+
</details>
431+
432+
---
433+
434+
**[⬆ Back to Top](#table-of-contents)**
435+
436+
#### 10. What is the output after clicking the button twice?
437+
438+
```javascript
439+
import { useState } from 'react';
440+
441+
export default function Form() {
442+
const [person, setPerson] = useState({
443+
name: 'John',
444+
age: 30
445+
});
446+
447+
function handleClick() {
448+
person.age = person.age + 1;
449+
setPerson(person);
450+
}
451+
452+
return (
453+
<>
454+
<div>Age: {person.age}</div>
455+
<button onClick={handleClick}>Increment Age</button>
456+
</>
457+
);
458+
}
459+
```
460+
461+
- 1: Age: 32
462+
- 2: Age: 31
463+
- 3: Age: 30
464+
- 4: Error: Cannot mutate state
465+
466+
<details><summary><b>Answer</b></summary>
467+
<p>
468+
469+
##### Answer: 3
470+
471+
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.
472+
473+
Even though `person.age` is being incremented internally, the UI shows the initial value (30) because React never re-renders the component.
474+
475+
The correct approach is to create a new object:
476+
```javascript
477+
function handleClick() {
478+
setPerson({
479+
...person,
480+
age: person.age + 1
481+
});
482+
}
483+
```
484+
485+
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**.
486+
487+
</p>
488+
</details>
489+
490+
---
491+
492+
**[⬆ Back to Top](#table-of-contents)**
493+
494+
#### 11. What will be logged to the console?
495+
496+
```javascript
497+
import { useEffect, useState } from 'react';
498+
499+
export default function App() {
500+
const [count, setCount] = useState(0);
501+
502+
useEffect(() => {
503+
console.log('Effect A');
504+
return () => console.log('Cleanup A');
505+
}, [count]);
506+
507+
useEffect(() => {
508+
console.log('Effect B');
509+
return () => console.log('Cleanup B');
510+
}, []);
511+
512+
console.log('Render');
513+
514+
return <button onClick={() => setCount(count + 1)}>Count: {count}</button>;
515+
}
516+
```
517+
518+
**On initial mount:**
519+
- 1: "Render", "Effect A", "Effect B"
520+
- 2: "Effect A", "Effect B", "Render"
521+
- 3: "Render", "Effect B", "Effect A"
522+
- 4: "Effect A", "Cleanup A", "Effect B", "Render"
523+
524+
**After first button click:**
525+
- 1: "Render", "Cleanup A", "Effect A"
526+
- 2: "Cleanup A", "Render", "Effect A"
527+
- 3: "Render", "Effect A", "Cleanup A"
528+
- 4: "Render", "Cleanup B", "Effect B", "Cleanup A", "Effect A"
529+
530+
<details><summary><b>Answer</b></summary>
531+
<p>
532+
533+
##### Answer for initial mount: 1, Answer for first click: 1
534+
535+
**On initial mount:**
536+
1. Component renders first: "Render"
537+
2. After render, effects run in the order they're defined: "Effect A", "Effect B"
538+
3. Cleanup functions are registered but not executed
539+
540+
**After first button click:**
541+
1. State changes, component re-renders: "Render"
542+
2. Before running Effect A again (because `count` dependency changed), its cleanup runs: "Cleanup A"
543+
3. Effect A runs again: "Effect A"
544+
4. Effect B doesn't run because its dependency array `[]` is empty (no changes)
545+
5. Cleanup B is not called because Effect B doesn't re-run
546+
547+
**Key takeaways:**
548+
- Render happens first, then effects run
549+
- Effects run in the order they're defined
550+
- Cleanup runs before re-running an effect (when dependencies change)
551+
- Effects with empty dependency arrays only run once on mount
552+
553+
</p>
554+
</details>
555+
556+
---
557+
558+
**[⬆ Back to Top](#table-of-contents)**

0 commit comments

Comments
 (0)