Локальное состояние и хуки

Вот мы построили приложение на React с глобальным состоянием в хранилище Redux. Но пока есть неудобства. Загрузка лотов производится даже когда мы не выводим их на странице, вхолостую занимая ресурсы браузера. И ещё использование внешнего хранилища порой может быть лишним.

Сегодня рассмотрим варианты хранения локального состояния внутри компонента. Код побочных эффектов сделаем выполняемым только при необходимости. И рассмотрим внутреннюю работу хуков React для использования побочных эффектов в функциональных компонентах:

  • 00:00:48 - Постановка задачи
  • 00:02:19 - Глобальное и локальное состояния
  • 00:05:30 - Ручное использование полей
  • 00:09:08 - Локальное состояние в Component
  • 00:11:13 - Создание таймера
  • 00:13:32 - Вынесение контейнерного компонента
  • 00:16:07 - Плюсы и минусы
  • 00:17:47 - Мотивация введения хуков
  • 00:19:43 - Состояние через хук useState
  • 00:21:52 - Устройство и работа хуков
  • 00:26:41 - Компонент React.Fragment
  • 00:29:59 - Побочные эффекты в useEffect
  • 00:37:05 - Перенос загрузки лотов
  • 00:39:25 - Уточнение зависимости
  • 00:40:31 - Добавление флага загрузки
  • 00:42:46 - Вынесение подписки на цены
  • 00:48:30 - Вынесение контейнеров
  • 00:49:54 - Многошаговый процесс подгрузки
  • 00:56:32 - Локальное хранение лотов
  • 01:04:21 - Замена Redux на useReducer
  • 01:06:26 - Обзор результата
  • 01:07:41 - Комбинация подходов

А в следующем эпизоде рассмотрим маршрутизацию через History API браузера.

Скрытый контент
Комментарии (16)
Александр Кулик

Супер

Ответить
Arunas

Спасибо.

Ответить
Кропотов Александр

Спасибо! Вообще получается что классы или функциональные компоненты это разные парадигмы разработки на Реакте, и мне кажется идет уход от классов да и от Редакса. Многие вещи решаются с помощью кастомных хуков, например та же авторизация. Вот тут много примеров есть https://usehooks.com, кому интересно. Т.е глобальные данные упаковываются либо в оборачивающий компонент, ну или в компонент высшего порядка. Но конечно, это вопрос личных предпочтений, как писать. Но вообще это чем то напомнило мидлвары в бэкенде.

Ответить
Дмитрий Елисеев

Да, изначально были классы с методами жизненного цикла, но сейчас рекомендуют переходить на более простые функции с хуками.

А по хранению данных часто используют локальное состояние и контексты. Но многие крупные проекты всё равно делают со внешними хранилищами если им требуется выделенная централизованная модель данных.

В этом эпизоде заметно, что простой виджет Clock спокойно перешёл на локальное состояние. Но как только мы всё состояние с лотами и экшены со stream и api перенесли внутрь Lots этот компонент сразу стал монструозным и почти нетестируемым. Так что не всегда очевидно что будет удобнее.

Ответить
rodigy

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

Ответить
Дмитрий Елисеев

То есть этот компонент:

function ClockContainer () {
  const [time, setTime] = React.useState(new Date())

  React.useEffect(() => {
    const interval = setInterval(() => setTime(new Date()), 1000)
    return () => clearInterval(interval)
  }, [setTime])

  return <Clock time={time} />
}

Выглядит более громоздким, чем этот?

class ClockContainer extends React.Component {
  constructor () {
    super()
    this.state = {
      time: new Date()
    }
    this.tick = this.tick.bind(this)
  }

  tick () {
    this.setState({
      time: new Date() 
    })
  }

  componentDidMount () {
    this.interval = setInterval(this.tick, 1000)
  }

  componentWillUnmount () {
    clearInterval(this.interval)
  }

  render () {
    return <Clock time={this.state.time} />
  }
}
Ответить
rodigy

В классах кода больше, но и ясности больше что происходит

Ответить
Aёct'ann

Но если знать, как работают хуки, то и в функциональном компоненте все предельно ясно

Ответить
Андрей

Дмитрий, спасибо за уроки, подскажите что за шрифт в самой программе idea?

Ответить
Дмитрий Елисеев

Интерфейс шрифтом Tahoma
В редакторе DejaVu Sans Mono

Ответить
Андрей

Благодарю

Ответить
kashamamina

А если вот будет отдельная страница с лотом, куда нужно будет вывести данные о лоте, то как это правильно сделать? Получается, будет компонент LotPage и он будет с адресной строки брать айди лота? А потом нужно сделать запрос на сервер чтобы получить этот лот, но где именно мы будем это делать, это же вроде как не экшен (в плане что вместе с экшенами мы его не поместим), состояние он никак не меняет, но запрос асинхронный? Плюс что если загрузка лота будет тоже такая продвинутая, как и у лотов?

Я думал что можно этот запрос сделать эффектом, вынести в контейнерный компонент с локальным состояниям где будет также вся эта продвинутая загрузка, но а что если я захочу протестировать этот эффект, вынести его куда-то и куда тогда? Так же каким образом этот контейнерный компонент будет получать доступ к объекту api, через пропсы его передавать?

Как бы вы это сделали?

Ответить
Дмитрий Елисеев

Да, LotPage в плане состояния и загрузки будет работать абсолютно также как Lots. Он будет брать id из маршрутизатора и у себя подгружать текущий лот.

Если с api всё будет в одном компоненте, то тогда со всеми хуками нужно будет тестировать весь компонент через React Testing Library. И там подменять переменную global.fetch или модуль api на заглушку, как мы делали в эпизоде про регистрацию.

Ответить
kashamamina

спасибо

Ответить
S.Polessky

Несколько вопросов по реализации функции resolveDispatcher() (Тайминг: 22:16) (Скриншот)

Интересно, для чего поступили именно так:

1) Зачем такая "сложная" проверка в if с использованием инверсии? Короче, читабельный же: if (dispatcher === null).

2) Зачем выброс исключения обернули в отдельную область видимости {}?

3) Почему используют var, а не let? Для совместимости?

Ответить
Дмитрий Елисеев

Да, весьма странный подход. Надо спросить у авторов этого кода.

Ответить
Зарегистрируйтесь или войдите чтобы оставить комментарий

Или войти через:

Google
GitHub
Yandex
MailRu