Skip to content

Commit 5be102a

Browse files
committed
Wrong way (Part 1): Let's fetching data from an api and having 2 counters for the todos: ongoing and done
1 parent c4f1f7b commit 5be102a

File tree

1 file changed

+47
-7
lines changed

1 file changed

+47
-7
lines changed

src/Todo/TodoList.js

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,73 @@
1-
import {useState} from 'react';
1+
import {useEffect, useState} from 'react';
22
import {v4} from 'uuid';
33

44
function createTodo(title, done) {
55
return {id: v4(), title, done};
66
}
77

8-
const TodoList = () => {
9-
const [todos, setTodoList] = useState(() => [
8+
const fetchTodosFromApi = async () => {
9+
return [
1010
createTodo('Frozen yoghurt', false),
1111
createTodo('Ice cream sandwich', false),
1212
createTodo('Eclair', false),
1313
createTodo('Cupcake', false),
1414
createTodo('Gingerbread', false),
15-
]);
15+
];
16+
};
17+
18+
const TodoList = () => {
19+
const [todos, setTodoList] = useState([]);
20+
useEffect(function callApi() {
21+
//1. Imagine that we are calling an api to get todos
22+
fetchTodosFromApi().then(list => setTodoList(list));
23+
}, []);
24+
25+
//2. And we have a state for 2 counter
26+
const [ongoingCount, setOngoingCount] = useState(0);
27+
const [doneCount, setDoneCount] = useState(0);
28+
29+
// 3. We will update the counters when the list changes
30+
useEffect(function refreshCounters() {
31+
setDoneCount(todos.filter(t => t.done).length);
32+
setOngoingCount(todos.filter(t => !t.done).length);
33+
}, [todos]);
34+
35+
//4. See how many render ?
36+
console.log('render with', {total: todos.length, ongoingCount, doneCount});
37+
// On first load it renders 3 times:
38+
// render with { total: 0, ongoingCount: 0, doneCount: 0 } ==> Before "callApi"
39+
// render with { total: 5, ongoingCount: 0, doneCount: 0 } ==> After "callApi"
40+
// render with { total: 5, ongoingCount: 5, doneCount: 0 } ==> After "refreshCounters". React is smart an only re-render when the value really changes. Here "doneCount" is "updated" from 0 to 0, so it does not re-render
41+
42+
// When I set one of the todos as "done", it re-renders only 2 times (but 3 things changed: todos, ongoingCount and doneCount, do you know why?)
43+
// render with { total: 5, ongoingCount: 5, doneCount: 0 } ==> when setTodoList is called
44+
// render with { total: 5, ongoingCount: 4, doneCount: 1 } ==> when setDoneCount/setOngoingCount is called (after useEffect)
45+
46+
//2 conclusions:
47+
// - We have too many render!
48+
// - React seems to wait the end of the useEffect to re-render the component (smart!)
1649

1750
const addEmptyTodo = () => setTodoList([createTodo('Relax! Edition will come...', false), ...todos]);
51+
const markAsDone = (index) => setTodoList([...todos.slice(0, index), {
52+
...todos[index],
53+
done: true,
54+
}, ...todos.slice(index + 1)]);
55+
1856

1957
return <>
2058
<table>
2159
<thead>
2260
<tr>
23-
<th rowSpan={2} align="left">My todos <button onClick={addEmptyTodo}>Add</button></th>
61+
<th rowSpan={2} align="left">My todos ({ongoingCount} ongoing /{doneCount} done)
62+
<button onClick={addEmptyTodo}>Add</button>
63+
</th>
2464
</tr>
2565
</thead>
2666
<tbody>
27-
{todos.map((todo) => (
67+
{todos.map((todo, index) => (
2868
<tr key={todo.id}>
2969
<td>{todo.title}</td>
30-
<td><input type="checkbox" value="1" checked={todo.done} disabled={true}/></td>
70+
<td><input type="checkbox" value="1" checked={todo.done} onChange={() => markAsDone(index)}/></td>
3171
</tr>
3272
))}
3373
</tbody>

0 commit comments

Comments
 (0)