Skip to content

Commit 6305136

Browse files
committed
feat: добавлен lesson 31 с примерами
1 parent 85464c3 commit 6305136

10 files changed

Lines changed: 513 additions & 22 deletions

File tree

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import React, { useState, memo, useMemo, useCallback } from "react"
2+
3+
const heavyDouble = (num) => {
4+
let result = 0;
5+
// Двойной цикл для большей задержки
6+
for (let i = 0; i < 100000; i++) {
7+
for (let j = 0; j < 10000; j++) {
8+
result = num * 2 + i + j; // Какая-то "полезная" работа
9+
}
10+
}
11+
return result;
12+
}
13+
14+
export default function ExamplePage() {
15+
const [count, setCount] = useState(0)
16+
const [name, setName] = useState('Alla')
17+
const [isAdmin, setIsAdmin] = useState(false)
18+
const [dark, setDark] = useState(false)
19+
20+
const themeStyle = {
21+
backgroundColor: dark ? 'tomato' : 'white'
22+
}
23+
24+
const setDarkTheme = () => {
25+
setDark(prev => !prev)
26+
}
27+
28+
const memoUser = useMemo(() => ({
29+
name, isAdmin
30+
}), [name, isAdmin])
31+
32+
const incrementCount = () => {
33+
setCount(count => count + 1)
34+
}
35+
36+
const resetCount = useCallback(() => {
37+
setCount(0)
38+
}, [])
39+
40+
const doubleCount = useMemo(() => heavyDouble(count), [count])
41+
42+
return (
43+
<div style={{...themeStyle}}>
44+
<Title user={memoUser} resetCount={resetCount}/>
45+
<CountInfo count={doubleCount}/>
46+
<button onClick={incrementCount}> + 1 </button>
47+
<button onClick={setDarkTheme}>Dark</button>
48+
</div>
49+
)
50+
}
51+
52+
const Title = React.memo(({user, resetCount}) => {
53+
console.log('Title rendered')
54+
const {name, isAdmin} = user
55+
56+
return (
57+
<>
58+
<h1>{isAdmin? name : 'Example'}</h1>
59+
<button onClick={resetCount}>RESET</button>
60+
</>
61+
)
62+
})
63+
64+
const CountInfo = ({count}) => <h3>Count value {count}</h3>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useState } from "react"
2+
3+
// const heavyDouble = (num) => {
4+
// let result = 0;
5+
// // Двойной цикл для большей задержки
6+
// for (let i = 0; i < 100000; i++) {
7+
// for (let j = 0; j < 10000; j++) {
8+
// result = num * 2 + i + j; // Какая-то "полезная" работа
9+
// }
10+
// }
11+
// return result;
12+
// }
13+
14+
export default function ExamplePage() {
15+
const [count, setCount] = useState(0)
16+
17+
const incrementCount = () => {}
18+
19+
return (
20+
<>
21+
<Title />
22+
<CountInfo />
23+
<button> + 1 </button>
24+
</>
25+
)
26+
}
27+
28+
const Title = () => {
29+
<h1>Example</h1>
30+
}
31+
32+
const CountInfo = ({count}) => <h3>Count value {count}</h3>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { memo } from "react"
2+
3+
// Дочерний компонент, который мы хотим оптимизировать
4+
export const CounterButton = memo(({ onClick, label }) => {
5+
console.log('Кнопка перерендерилась:', label);
6+
7+
return <button onClick={onClick}>{label}</button>;
8+
});
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { useCallback, useState } from "react"
2+
3+
import { CounterButton } from "./CounterButton"
4+
5+
function UseCallbackPage1() {
6+
7+
const [counter, setCounter] = useState(0)
8+
9+
const incrementCounter = useCallback(() => {
10+
setCounter(counter + 1)
11+
}, [counter])
12+
13+
return (
14+
<>
15+
<h1>useCallback</h1>
16+
<h3>{counter}</h3>
17+
<button onClick={incrementCounter}> + 1 </button>
18+
</>
19+
)
20+
}
21+
22+
function UseCallbackPage() {
23+
const [counter, setCounter] = useState(0);
24+
const [dummy, setDummy] = useState(0);
25+
26+
// Функция не меняется между рендерами
27+
const incrementCounter = useCallback(() => {
28+
setCounter(prev => prev + 1); // Используем функциональное обновление
29+
}, []); // Пустые зависимости - функция стабильна
30+
31+
// Без useCallback - создается новая функция при каждом рендере
32+
const badIncrement = () => {
33+
setCounter(prev => prev + 1);
34+
};
35+
36+
return (
37+
<div>
38+
<h1>useCallback</h1>
39+
<h3>Счетчик: {counter}</h3>
40+
41+
{/* С useCallback - кнопка не перерендеривается лишний раз */}
42+
<CounterButton
43+
onClick={incrementCounter}
44+
label="+1 (с useCallback)"
45+
/>
46+
47+
{/* Без useCallback - кнопка перерендеривается всегда */}
48+
<CounterButton
49+
onClick={badIncrement}
50+
label="+1 (без useCallback)"
51+
/>
52+
53+
<button onClick={() => setDummy(d => d + 1)}>
54+
Принудительный перерендер ({dummy})
55+
</button>
56+
57+
<p>Откройте консоль и нажимайте "Принудительный перерендер"</p>
58+
</div>
59+
);
60+
}
61+
62+
export default UseCallbackPage
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { useEffect, useState } from "react"
2+
3+
// 1. Не передать ничего
4+
function UseEffectPage1() {
5+
const [, setValue] = useState({})
6+
7+
useEffect(() => {
8+
// Эффект срабатывает при монтировании компонента и при каждом рендере.
9+
console.log('Срабатываю на каждый рендер')
10+
})
11+
12+
return (
13+
<div>
14+
<h1>useEffect</h1>
15+
<button onClick={() => setValue({})}>Обновить состояние</button>
16+
</div>
17+
)
18+
}
19+
20+
// 2. Передать пустой массив - []
21+
function UseEffectPage2() {
22+
const [count, setCount] = useState(0)
23+
24+
useEffect(() => {
25+
// Эффект срабатывает только при монтировании компонента.
26+
console.log('Сработаю 1 раз')
27+
}, [])
28+
29+
return (
30+
<div>
31+
<h1>useEffect</h1>
32+
<h3>{count}</h3>
33+
<button onClick={() => setCount((prev) => prev + 1)}> + 1</button>
34+
</div>
35+
)
36+
}
37+
38+
// 3. Передать массив с переменными для отслеживания - [value]
39+
function UseEffectPage3({ userId = 2 }) {
40+
const [user, setUser] = useState(null);
41+
const [isLoading, setIsLoading] = useState(false);
42+
43+
// Эффект, зависящий от userId
44+
useEffect(() => {
45+
if (!userId) return;
46+
47+
console.log('Сработаю при монтировании и изменении userId')
48+
49+
fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
50+
.then(response => response.json)
51+
.then(json => setUser(json))
52+
.catch(error => console.error('Ошибка загрузки:', error))
53+
.finally(() => setIsLoading(false))
54+
55+
}, [userId]); // Запускается заново, если изменился userId
56+
57+
if (isLoading) return <div>Загрузка...</div>;
58+
if (!user) return <div>Выберите пользователя</div>;
59+
60+
return (
61+
<div>
62+
<h1>useEffect</h1>
63+
<h3>{user.name}</h3>
64+
<p>{user.email}</p>
65+
</div>
66+
);
67+
}
68+
69+
// 4. Пример очистки функции с интервалом
70+
function UseEffectPage() {
71+
const [time, setTime] = useState(new Date().toLocaleTimeString());
72+
73+
useEffect(() => {
74+
console.log('✅ Таймер запущен');
75+
76+
// Запускаем интервал при монтировании
77+
const intervalId = setInterval(() => {
78+
const currentTime = new Date().toLocaleTimeString();
79+
setTime(currentTime);
80+
console.log('🕒 Время обновлено:', currentTime);
81+
}, 1000);
82+
83+
// Функция очистки
84+
return () => {
85+
console.log('Таймер остановлен');
86+
clearInterval(intervalId);
87+
};
88+
}, []); // Пустой массив - эффект только при монтировании/размонтировании
89+
90+
return (
91+
<div>
92+
<h3>Текущее время: {time}</h3>
93+
</div>
94+
);
95+
}
96+
97+
98+
export default UseEffectPage
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { useMemo } from 'react';
2+
3+
export default function TodoList({ todos, filter }) {
4+
// БЕЗ useMemo: фильтрация происходит при каждом рендере
5+
// const visibleTodos = todos.filter(todo => {
6+
// console.log('🔴 ФИЛЬТРАЦИЯ (без useMemo)'); // Это будет логироваться часто
7+
// console.log('--------------------'); // Это будет логироваться часто
8+
// return filter === 'all' ||
9+
// (filter === 'completed' && todo.completed) ||
10+
// (filter === 'active' && !todo.completed);
11+
// });
12+
13+
// С useMemo: фильтрация происходит только при изменении todos или filter
14+
const visibleTodos = useMemo(() => {
15+
console.log('🟢 ФИЛЬТРАЦИЯ (с useMemo)'); // Логируется только при изменении зависимостей
16+
return todos.filter(todo =>
17+
filter === 'all' ||
18+
(filter === 'completed' && todo.completed) ||
19+
(filter === 'active' && !todo.completed)
20+
);
21+
}, [todos, filter]); // Зависимости: todos и filter
22+
23+
return (
24+
<ul>
25+
{visibleTodos.map(todo => (
26+
<li key={todo.id}>{todo.text}</li>
27+
))}
28+
</ul>
29+
);
30+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { useState } from 'react';
2+
import TodoList from "./TodoList";
3+
4+
const todos = [
5+
{ id: 1, text: 'Изучить React', completed: true },
6+
{ id: 2, text: 'Написать компонент', completed: false },
7+
{ id: 3, text: 'Разобраться с хуками', completed: false },
8+
{ id: 4, text: 'Создать приложение', completed: true },
9+
{ id: 5, text: 'Оптимизировать производительность', completed: false },
10+
{ id: 6, text: 'Изучить useMemo', completed: true },
11+
{ id: 7, text: 'Понять useCallback', completed: false },
12+
{ id: 8, text: 'Сделать домашнее задание', completed: false }
13+
];
14+
15+
function UseMemoPage() {
16+
const [filter, setFilter] = useState('all');
17+
18+
return (
19+
<div style={{ padding: '20px' }}>
20+
<h1>useMemo</h1>
21+
22+
{/* Кнопки фильтров */}
23+
<div style={{ marginBottom: '20px' }}>
24+
<h3>Выберите фильтр:</h3>
25+
<button onClick={() => setFilter('all')} style={{marginRight: '20px'}}>Все задачи</button>
26+
<button onClick={() => setFilter('active')} style={{marginRight: '20px'}}>Активные</button>
27+
<button onClick={() => setFilter('completed')}>Завершенные</button>
28+
</div>
29+
30+
{/* Компонент со списком задач */}
31+
<TodoList todos={todos} filter={filter} />
32+
</div>
33+
);
34+
}
35+
36+
export default UseMemoPage;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useState } from 'react'
2+
import code from './../../assets/useState.png'
3+
4+
function UseStatePage() {
5+
const [count, setCount] = useState(0)
6+
7+
const incrementCount = () => setCount((count) => count + 1)
8+
9+
// Функциональное обновление
10+
const decrementCount = () => setCount((prev) => prev - 1)
11+
12+
return (
13+
<>
14+
<h1>Hook useState</h1>
15+
16+
<h3>{count}</h3>
17+
18+
<div style={{display: 'flex', justifyContent: 'space-around', marginBottom: '20px'}}>
19+
<button onClick={incrementCount}> + 1 </button>
20+
21+
<button onClick={decrementCount}> - 1 </button>
22+
</div>
23+
24+
<img src={code} alt='code' width='500px'/>
25+
</>
26+
)
27+
}
28+
29+
export default UseStatePage
-54.4 KB
Loading

0 commit comments

Comments
 (0)