Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 71 additions & 0 deletions lessons/lesson31/examples/examplePage/ExamplePage.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/display-name */
/* eslint-disable react/react-in-jsx-scope */
import React, { useState, memo, useMemo, useCallback } from "react";

const heavyDouble = (num) => {
let result = 0;
// Двойной цикл для большей задержки
for (let i = 0; i < 100000; i++) {
for (let j = 0; j < 10000; j++) {
result = num * 2 + i + j; // Какая-то "полезная" работа
}
}
return result;
};

export default function ExamplePage() {
const [count, setCount] = useState(0);
const [name, setName] = useState("Alla");
const [isAdmin, setIsAdmin] = useState(false);
const [dark, setDark] = useState(false);

const themeStyle = {
backgroundColor: dark ? "tomato" : "white",
};

const setDarkTheme = () => {
setDark((prev) => !prev);
};

const memoUser = useMemo(
() => ({
name,
isAdmin,
}),
[name, isAdmin]
);

const incrementCount = () => {
setCount((count) => count + 1);
};

const resetCount = useCallback(() => {
setCount(0);
}, []);

const doubleCount = useMemo(() => heavyDouble(count), [count]);

return (
<div style={{ ...themeStyle }}>
<Title user={memoUser} resetCount={resetCount} />
<CountInfo count={doubleCount} />
<button onClick={incrementCount}> + 1 </button>
<button onClick={setDarkTheme}>Dark</button>
</div>
);
}

const Title = React.memo(({ user, resetCount }) => {
console.log("Title rendered");
const { name, isAdmin } = user;

return (
<>
<h1>{isAdmin ? name : "Example"}</h1>
<button onClick={resetCount}>RESET</button>
</>
);
});

const CountInfo = ({ count }) => <h3>Count value {count}</h3>;
34 changes: 34 additions & 0 deletions lessons/lesson31/examples/examplePage/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/react-in-jsx-scope */
import { useState } from "react";

// const heavyDouble = (num) => {
// let result = 0;
// // Двойной цикл для большей задержки
// for (let i = 0; i < 100000; i++) {
// for (let j = 0; j < 10000; j++) {
// result = num * 2 + i + j; // Какая-то "полезная" работа
// }
// }
// return result;
// }

export default function ExamplePage() {
const [count, setCount] = useState(0);

const incrementCount = () => {};

return (
<>
<Title />
<CountInfo />
<button> + 1 </button>
</>
);
}

const Title = () => {
<h1>Example</h1>;
};

const CountInfo = ({ count }) => <h3>Count value {count}</h3>;
11 changes: 11 additions & 0 deletions lessons/lesson31/examples/useCallbackPage/CounterButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/display-name */
/* eslint-disable react/react-in-jsx-scope */
import { memo } from "react";

// Дочерний компонент, который мы хотим оптимизировать
export const CounterButton = memo(({ onClick, label }) => {
console.log("Кнопка перерендерилась:", label);

return <button onClick={onClick}>{label}</button>;
});
56 changes: 56 additions & 0 deletions lessons/lesson31/examples/useCallbackPage/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/* eslint-disable react/react-in-jsx-scope */
import { useCallback, useState } from "react";

import { CounterButton } from "./CounterButton";

function UseCallbackPage1() {
const [counter, setCounter] = useState(0);

const incrementCounter = useCallback(() => {
setCounter(counter + 1);
}, [counter]);

return (
<>
<h1>useCallback</h1>
<h3>{counter}</h3>
<button onClick={incrementCounter}> + 1 </button>
</>
);
}

function UseCallbackPage() {
const [counter, setCounter] = useState(0);
const [dummy, setDummy] = useState(0);

// Функция не меняется между рендерами
const incrementCounter = useCallback(() => {
setCounter((prev) => prev + 1); // Используем функциональное обновление
}, []); // Пустые зависимости - функция стабильна

// Без useCallback - создается новая функция при каждом рендере
const badIncrement = () => {
setCounter((prev) => prev + 1);
};

return (
<div>
<h1>useCallback</h1>
<h3>Счетчик: {counter}</h3>

{/* С useCallback - кнопка не перерендеривается лишний раз */}
<CounterButton onClick={incrementCounter} label="+1 (с useCallback)" />

{/* Без useCallback - кнопка перерендеривается всегда */}
<CounterButton onClick={badIncrement} label="+1 (без useCallback)" />

<button onClick={() => setDummy((d) => d + 1)}>
Принудительный перерендер ({dummy})
</button>

<p>Откройте консоль и нажимайте Принудительный перерендер</p>
</div>
);
}

export default UseCallbackPage;
98 changes: 98 additions & 0 deletions lessons/lesson31/examples/useEffectPage/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/* eslint-disable react/prop-types */
/* eslint-disable react/react-in-jsx-scope */
import { useEffect, useState } from "react";

// 1. Не передать ничего
function UseEffectPage1() {
const [, setValue] = useState({});

useEffect(() => {
// Эффект срабатывает при монтировании компонента и при каждом рендере.
console.log("Срабатываю на каждый рендер");
});

return (
<div>
<h1>useEffect</h1>
<button onClick={() => setValue({})}>Обновить состояние</button>
</div>
);
}

// 2. Передать пустой массив - []
function UseEffectPage2() {
const [count, setCount] = useState(0);

useEffect(() => {
// Эффект срабатывает только при монтировании компонента.
console.log("Сработаю 1 раз");
}, []);

return (
<div>
<h1>useEffect</h1>
<h3>{count}</h3>
<button onClick={() => setCount((prev) => prev + 1)}> + 1</button>
</div>
);
}

// 3. Передать массив с переменными для отслеживания - [value]
function UseEffectPage3({ userId = 2 }) {
const [user, setUser] = useState(null);
const [isLoading, setIsLoading] = useState(false);

// Эффект, зависящий от userId
useEffect(() => {
if (!userId) return;

console.log("Сработаю при монтировании и изменении userId");

fetch(`https://jsonplaceholder.typicode.com/users/${userId}`)
.then((response) => response.json)
.then((json) => setUser(json))
.catch((error) => console.error("Ошибка загрузки:", error))
.finally(() => setIsLoading(false));
}, [userId]); // Запускается заново, если изменился userId

if (isLoading) return <div>Загрузка...</div>;
if (!user) return <div>Выберите пользователя</div>;

return (
<div>
<h1>useEffect</h1>
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
);
}

// 4. Пример очистки функции с интервалом
function UseEffectPage() {
const [time, setTime] = useState(new Date().toLocaleTimeString());

useEffect(() => {
console.log("✅ Таймер запущен");

// Запускаем интервал при монтировании
const intervalId = setInterval(() => {
const currentTime = new Date().toLocaleTimeString();
setTime(currentTime);
console.log("🕒 Время обновлено:", currentTime);
}, 1000);

// Функция очистки
return () => {
console.log("Таймер остановлен");
clearInterval(intervalId);
};
}, []); // Пустой массив - эффект только при монтировании/размонтировании

return (
<div>
<h3>Текущее время: {time}</h3>
</div>
);
}

export default UseEffectPage;
33 changes: 33 additions & 0 deletions lessons/lesson31/examples/useMemoPage/TodoList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/* eslint-disable react/react-in-jsx-scope */
/* eslint-disable react/prop-types */
import { useMemo } from "react";

export default function TodoList({ todos, filter }) {
// БЕЗ useMemo: фильтрация происходит при каждом рендере
// const visibleTodos = todos.filter(todo => {
// console.log("🔴 ФИЛЬТРАЦИЯ (без useMemo)"); // Это будет логироваться часто
// console.log("--------------------"); // Это будет логироваться часто
// return filter === "all" ||
// (filter === "completed" && todo.completed) ||
// (filter === "active" && !todo.completed);
// });

// С useMemo: фильтрация происходит только при изменении todos или filter
const visibleTodos = useMemo(() => {
console.log("🟢 ФИЛЬТРАЦИЯ (с useMemo)"); // Логируется только при изменении зависимостей
return todos.filter(
(todo) =>
filter === "all" ||
(filter === "completed" && todo.completed) ||
(filter === "active" && !todo.completed)
);
}, [todos, filter]); // Зависимости: todos и filter

return (
<ul>
{visibleTodos.map((todo) => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
);
}
47 changes: 47 additions & 0 deletions lessons/lesson31/examples/useMemoPage/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* eslint-disable react/react-in-jsx-scope */
import { useState } from "react";
import TodoList from "./TodoList";

const todos = [
{ id: 1, text: "Изучить React", completed: true },
{ id: 2, text: "Написать компонент", completed: false },
{ id: 3, text: "Разобраться с хуками", completed: false },
{ id: 4, text: "Создать приложение", completed: true },
{ id: 5, text: "Оптимизировать производительность", completed: false },
{ id: 6, text: "Изучить useMemo", completed: true },
{ id: 7, text: "Понять useCallback", completed: false },
{ id: 8, text: "Сделать домашнее задание", completed: false },
];

function UseMemoPage() {
const [filter, setFilter] = useState("all");

return (
<div style={{ padding: "20px" }}>
<h1>useMemo</h1>

{/* Кнопки фильтров */}
<div style={{ marginBottom: "20px" }}>
<h3>Выберите фильтр:</h3>
<button
onClick={() => setFilter("all")}
style={{ marginRight: "20px" }}
>
Все задачи
</button>
<button
onClick={() => setFilter("active")}
style={{ marginRight: "20px" }}
>
Активные
</button>
<button onClick={() => setFilter("completed")}>Завершенные</button>
</div>

{/* Компонент со списком задач */}
<TodoList todos={todos} filter={filter} />
</div>
);
}

export default UseMemoPage;
36 changes: 36 additions & 0 deletions lessons/lesson31/examples/useStatePage/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/* eslint-disable react/react-in-jsx-scope */
import { useState } from "react";
import code from "./../../assets/useState.png";

function UseStatePage() {
const [count, setCount] = useState(0);

const incrementCount = () => setCount((count) => count + 1);

// Функциональное обновление
const decrementCount = () => setCount((prev) => prev - 1);

return (
<>
<h1>Hook useState</h1>

<h3>{count}</h3>

<div
style={{
display: "flex",
justifyContent: "space-around",
marginBottom: "20px",
}}
>
<button onClick={incrementCount}> + 1 </button>

<button onClick={decrementCount}> - 1 </button>
</div>

<img src={code} alt="code" width="500px" />
</>
);
}

export default UseStatePage;
Binary file added lessons/lesson31/images/react-lifecycle.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading