18 - Что такое Inversion of control и причём здесь callbacks?

Оценить качество материала и подачу материала автором видео:

Front-end

Трудоустройтесь middle front-end разработчиком на React JS (TypeScript) за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Front-end

Back-end

Трудоустройтесь middle back-end разработчиком за 12-16 месяцев обучения с ежедневной менторской поддержкой в формате видео 1 на 1 и коммерческими проектами в портфолио

Перейти на курс
Back-end

Карьерный бустер

Получите коммерческий опыт на реальных стартапах, прокачайте tech & soft навыки, научитесь работать в команде, проходить собеседования и получите первую работу в IT!

Перейти на курс
Карьерный бустер

Основы Front-end

Сделайте первый шаг в IT, освоив базовые знания разработки и научившись создавать небольшие проекты на JavaScript

Перейти на курс
Основы Front-end

Основы Back-end

Сделайте первый шаг в IT, освоив базовые знания разработки. Без опыта. Без математики. Только практика: JavaScript, SQL, Node JS, база данных

Перейти на курс
Основы Back-end

Callbacks

Автор конспекта: Стогниева Виктория

👨‍👦 Введение: Обычный порядок вещей

Представьте компоненты как семью:

  • 🎯 Родитель → управляет дочерним компонентом через пропсы
  • 🔻 Стандартный поток → сверху вниз
  • Проблема: дочерний компонент не может напрямую вызывать родительские функции

По правилам React, ребенок не должен зависеть от родителя на уровне кода — то есть, он не может его импортировать и напрямую вызывать его функции. Так как же ему «позвонить» и попросить о помощи?

📱 Решение: "телефончик" для дочернего компонента

💡

Этот телефончик — это специальная функция, которую мы передаем из родительского компонента в дочерний. Эта функция называется колбэк-функция (от англ. callback — обратный вызов).

Аналогия с setTimeout:

javascript
// Мы передаем функцию ("телефончик") window.setTimeout
window.setTimeout(() => {
  // Эта функция будет вызвана позже
}, 4000)

В React:

  • 📞 Колбэк-функция → это "телефончик"
  • 🔄 Родитель передает функцию через пропсы
  • 📢 Ребенок вызывает функцию когда нужно

⚡ Как это работает: два подхода

🎯 Прямое управление (стандартный поток): AdependsB

  • Зависимость: Родитель → Ребенок (импорт через import)
  • Управление: Родитель → Ребенок (родитель вызывает функции ребенка)

🔄 Инверсия управления (с колбэком): BcontrolsA

  • Зависимость: Родитель → Ребенок
  • Управление: Ребенок → Родитель (ребенок вызывает колбэк)
💡

Инверсия управления (Inversion of Control, IoC) — это архитектурный паттерн, при котором объект, от которого мы зависим, начинает управлять нами, а не как обычно — мы им.

🎮 Практический пример: игра "Счетчик до пяти"

Цель игры:

  • Есть родительский компонент Game, который решает, что показывать на экране: счетчик или картинку со слоником.
  • Есть дочерний компонент Counter, который считает клики по кнопке до 5.
  • Когда Counter досчитает до 5, он должен сообщить Game, чтобы тот показал слоника.

Решение:

  • Шаг 1: родительский компонент Game. Родительский компонент Game — это наш «информационный эксперт». Он отвечает за общую логику игры и хранит в своем состоянии, какая «страница» сейчас активна: счетчик или слоник.
Game.tsx
export function Game() {
  const [activePage, setActivePage] = useState("counter")
 
  // 📞 Создаем "телефончик"
  const handleFinish = () => {
    setActivePage("elephant")
  }
 
  return (
    <div>
      {activePage === "counter" && (
        // 📱 Передаем телефончик ребенку
        <Counter onFinish={handleFinish} />
      )}
      {activePage === "elephant" && <Elephant />}
    </div>
  )
}
  • Шаг 2: передача "телефончика" (). Game создает функцию handleFinish, которая меняет страницу на слоника, и передает ее в Counter через проп onFinish. Эта функция и есть наш «телефончик».

  • Шаг 3: работа ребенка (). Компонент Counter абсолютно самодостаточен. Он имеет свое внутреннее состояние для счетчика и ничего не знает ни о Game, ни о слониках. Его единственная задача — считать до пяти. Когда счетчик достигает нужного значения, он проверяет, передали ли ему «телефончик» (props.onFinish). Если да — он «звонит».

Counter.tsx
export function Counter(props) {
  const [value, setValue] = useState(1)
 
  const handleClick = () => {
    const newValue = value + 1
    setValue(newValue)
 
    if (newValue === 5) {
      // 📢 Звоним родителю через "телефончик"
      if (props.onFinish) {
        props.onFinish()
      }
    }
  }
 
  return <button onClick={handleClick}>+ {value}</button>
}
  • Шаг 4: дочерний компонент Elephant
Elephant.tsx
export function Elephant(props) {
  return <div style={{ fontSize: "100px" }}>🐘</div>
}

🎯 Результат:

  • Пользователь кликает 5 раз
  • Counter видит, что счетчик равен 5
  • Counter вызывает props.onFinish()
  • Этот «звонок» активирует функцию handleFinish внутри компонента Game
  • handleFinish меняет состояние activePage на 'elephant'.
  • Компонент Game перерисовывается, Counter исчезает, а на его месте появляется слоник.🐘

Почему это так важно?

Главный принцип, который мы здесь соблюдаем, — это принцип информационного эксперта. Каждый компонент должен отвечать только за свою область знаний. Инверсия управления помогает нам этого достичь.

✅ Преимущества:

  • 🎯 Разделение ответственности → каждый компонент занимается своим делом
  • 🔄 Переиспользуемость → Counter можно использовать в разных местах
  • 📊 Предсказуемость → логика сосредоточена в одном месте
  • 🛡 Принцип информационного эксперта → каждый знает только о своей зоне ответственности

❌ Чего НЕ делать:

tsx
// ⚠️ НЕПРАВИЛЬНО - ребенок знает детали реализации родителя
;<Counter onFinish={setActivePage} />
 
// В компоненте Counter:
props.setActivePage("elephant") // ❌ Плохо - привязка к конкретной логике
💡

Итак, инверсия управления через колбэки — это простой, но невероятно мощный прием. Это как дать дочернему компоненту «телефончик» для связи с родителем, позволяя ему сообщить о важных событиях, не нарушая иерархии зависимостей.


🏠 Домашнее задание

Цель задания: Овладеть мощнейшим архитектурным принципом — инверсией управления — и научиться создавать компоненты, которые могут взаимодействовать через callback-функции.

Теоретические вопросы

1. Основы инверсии управления

  • Объясни своими словами, что такое инверсия управления
  • В чём разница между прямым управлением и инверсией управления?
  • Приведи 3 примера из жизни, где можно увидеть принцип инверсии управления (по аналогии с "телефончиком" из урока)

2. Callback функции

  • Что такое callback функция и почему она называется "функцией обратного вызова"?
  • Объясни на примере setTimeout, как работает инверсия управления
  • Почему callback функцию передают БЕЗ круглых скобок?

3. React компоненты

  • Как props помогают реализовать инверсию управления между родительским и дочерним компонентами?
  • Что такое "информационный эксперт" и как правильно распределить ответственности между компонентами?

Практические задания

Задание 1

Игра про слона

Напиши код игры из видеоурока:

  1. Создай компоненты Game.tsx, Counter.tsx и Elephant.tsx
  2. При клике на кнопку + счетчик должен увеличиваться на единицу
  3. Когда значение счетчика будет равно 5 покажи emoji слона (🐘)
🧠
Попробуй сделать данное задние без подглядывания в урок

Задание 2

Будем развивать нашу игру для детей.

  1. В компоненте Counter.tsx добавь заголовок "Нажми на кнопку 4 раза, чтобы увидеть слона", чтобы ребенок понимал что делать

  2. В компоненте Elephant.tsx добавь кнопку "Давай сыграем еще раз"

  • При клике по данной кнопке игра должна вернуться на страницу счетчика
  • Счётчик должен сброситься к первоначальному состоянию

Итоговый результат 🚀

res18-2

Задание 3

Продолжим развивать игру.

  1. В компоненте Elephant.tsx добавь локальное состояние в котором будем хранить вес слона
Elephant.tsx
const [weight, setWeight] = useState(100) // 100 условных единиц
  1. В компоненте Elephant.tsx добавь 2 кнопки при помощи которых ребенок будет кормить слона.
  • При нажатии на первую кнопку вес слона должен увеличиваться на 20, т.к. ребенок будет кормить слона полезной едой
  • При нажатии на вторую кнопку вес слона должен уменьшаться на 20, т.к. ребенок будет кормить слона вредной едой
  • С каждым нажатием на кнопку слон должен пропорционально изменяться (увеличиваться или уменьшаться) в зависимости от того на какую кнопку нажимает ребенок
Elephant.tsx
<div>
  <h1>Покорми слона</h1>
  <button onClick={handleFeedHealthyFood}>Кормить слона полезной едой 🥬🍉🍌</button>
  <br />
  <button onClick={handleFeedJunkFood}>Кормить слона вредной едой 🍔🍬🍕</button>
  <div /*код*/>🐘</div>
  <button /*код*/>Давай сыграем еще раз</button>
</div>

Итоговый результат 🚀

res18-3

Задание 4

  1. Создай компонент Congratulations.tsx, который нужно будет рендериться когда weight слона станет равным или больше 200.
  • В теле данного компонента напиши "🎉 Поздравляю! Твой слон наелся здоровой пищи и с улыбкой побежал играть с другими слонами🎉"
  • Добавь кнопку с сообщением "Давай сыграем еще раз и покормим другого слона", которая вернет ребенка на первоначальную страницу счетчика
  • Добавь радостный смайлик, чтобы ребенок обрадовался, т.к. он сделал все верно <div style={{ fontSize: "200px" }}>😊</div>
  1. Создай компонент GameOver.tsx, который нужно будет рендериться когда weight слона станет равным или меньше 20
  • В теле данного компонента напиши "У твоего слоника заболел живот и вместо того чтобы играть со своими друзьями он пошел к врачу. В следующий раз корми слона правильной пищей, чтобы слоник был здоров"
  • Добавь кнопку с сообщением "Но не расстраивайся. Давай сыграем еще раз", которая вернет ребенка на первоначальную страницу счетчика
  • Добавь грустный смайлик <div style={{ fontSize:'200px'}}>🥲</div>
  1. В компоненте Elephant.tsx удали кнопку "Давай сыграем еще раз", т.к. данную кнопку мы перенесли в компоненты Congratulations.tsx и GameOver.tsx

Итоговая логика такая. Если ребенок кормит слона правильной пищей, то слон растет и достигнув веса 200 ребенок видит сообщение о том что слон наелся и играет. Если ребенок кормит слона вредной пищей, то слон уменьшается и достигнув веса 20 ребенок видит сообщение, что слон пошел к врачу лечить живот

Итоговый результат 🚀

res18-4


💡

🔶 Помни: инверсия управления — это основа всех современных фреймворков и библиотек. От понимания этого принципа зависит твое архитектурное мышление как инженера.

🔶 Думай как архитектор: каждый компонент должен быть самодостаточным, но уметь "кричать" родителю о важных событиях через callback-и. Это ключ к созданию переиспользуемых компонентов.

🔶 Экспериментируй: добавляй console.log в callback-функции, наблюдай за потоком управления. Попробуй нарушить принципы — посмотри, что сломается. Только через ошибки приходит понимание.

🔶 Удачи, самурай! Освоив инверсию управления, ты сделаешь важный шаг от "кодера" к "инженеру". Этот принцип откроет тебе дверь в серьезную архитектуру. 🥷⚡

Боевой маршрут (React Путь Самурая: без альтернатив)

Видеоурок - 20 видео из 30