React Hooks — огляд можливостей нового API

Мене звати Володимир Симоненко. Я фронтенд-розробник у компанії PyTeam . Займаюся веб-розробкою 3 рокі. Загальний досвід у розробці програмного забезпечення на різних позиціях — 8 років. Нещодавно я робив доповідь на тему React Hooks і вирішив поділитися інформацією на DOU. Це стаття-огляд нових можливостей відомої та популярної бібліотеки для веб-інтерфейсів React.js і буде більш цікава тим, хто вже знайомий з реактом.

Hooks— нове API, що дозволяє писати функціональні компоненти зі станом та використовувати інші можливості реакту без написання класів. Доступно в прев'ю версії (поточна версія 16.8.0-alpha.1). Встановлюємо: npm i –S react@next.

Навіщо це потрібно

По-перше, це потрібно, щоб повторно використовувати логіку, що знаходиться в state, між компонентами. Для вирішення цієї задачі зазвичай використовують такі підходи: Higher-Order Components (HOC) і Render Props .

По-друге, хуки дозволяють розділити один компонент на більш дрібні функції в залежності від того, які частини пов'язані (наприклад, налаштування підписки або вибірка даних), замість примусового поділу на основі методів життєвого циклу.

По-третє, хуки дозволяють використовувати більше можливостей Reactбез класів. Класи складні для людей і для машин. У спостереженні facebookкласі є великою перешкодою при вивченні React. Необхідно зрозуміти, як працює this, а він не працює так, як в інших мовах програмування. Так само слід пам " ятати про прив'язана язку обробників подій. Без стабільних пропозицій синтаксису код виглядає дуже багатослівно. Також класі не дуже добре мінімізуються, і вони роблять гаряче перезавантаження (hotreload) ненадійним.

Hooks API

Базові:

Додаткові:

Напишемо кілька рядків

На основі хуків реакту напишемо власний (custom hook), який буде реагувати на зміну ширини області перегляду вікна браузера і буде повертати значення екрану. Такий хук буде корисним для адаптивного дизайну (responsive design).

Важливо! Угода про іменування призначених для користувача хуків: Сustom Hook — це функція JavaScript, ім'я якої починається з «use» і яка може віклікаті інші хуки.

Отже, створюємо функціональний компонент з локальним станом:

import { useState } from 'react'

export default function useBreakpoints() {
 const [points, setPoints] = useState(window.innerWidth)

 return points
}

Функція useStateповертає масив, у якому під індексом 0 знаходиться зміна, що буде зберігати stateта під індексом 1 повертає функцію, що буде змінюватиstate. Зверніть увагу, що pointsбуде мати ініційоване значення, яке ми передали в хук useState, а саме window.innerWidth. Якщо порівняти ці рядки з компонентом на основі класу, то ми маємо аналог ініціації змінної pointsу методі constructor,a setPoints— це аналог setState.

Створюємо обробник, який буде записувати актуальну ширину екрану:

...
const handleResize = () => setPoints(window.innerWidth)
...

Підписуємо обробник handleResizeна подію зміни документа:

import { useState, useEffect } from 'react'

export default function useBreakpoints() {
...
 useEffect(() => {
 window.addEventListener. ('resize', handleResize)
 return () => window.removeEventListener('resize', handleResize)
})
...
}

ХукuseEffectбуде викликатись декілька разів протягом життєвого циклу компонента — один раз після монтування (одразу як компонент буде додано у дерево) та шкірного разу, коли є необхідність оновлення. Якщо знову провести аналогію з компонентом на основі класу, то ми маємо componentDidMount, componentDidUpdate, componentWillUnmountв одному місці. Зверніть увагу: щоб уникнути memory leak,необхідно відписатись, коли компонент буде відмонтовано і знищено. Для цього необхідно повернути функцію «відписки» в контексті анонімної функції, що була передана в хукuseEffect, як це наведено у прикладі. Готово!

Порада

Щоб викликати функцію лише один раз після монтування, скористуйтеся таким трюком. Передайте іншим параметром пустий масив:

...
useEffect(() => handleResize(), [])
...

Іншим аргументом хук приймає масив об'єктів, що необхідно відстежувани. Такий механізм потрібен для контрольованих викликів. Тобто побічні ефекти в контексті хука будуть виконанні тільки при зміні значень, переданих у масив об'єктів.

Важливі правила

  1. Викликайте хуки тільки в середині функціональних компонентів.
  2. Викликайте хуки тільки на верхньому рівні функції, тобто на початку блоку. Не викликайте хуки в межах loops, conditionsчи nestedfunctions.

Для розробки є плагін eslint-plugin-react-hooks для лінтераESLint, що буде відстежувати виконання цих правил.

Як щодо тестування

З точки зору React, компонент, який використовує Hooks, — це звичайний компонент. Для тестування кастомних хуків можна використати утилітиreact-testing-library.Необхідно лише створити компонент і викликати в ньому хук. Далі можна симулювати подію зміни document viewта перевірити «вихлип» функції (даруйте за жаргон, не стримався ;) ).

import React from 'react'
import { render } from 'react-testing-library'
import useBreakpoints from './useBreakpoints'

const sizeSmall = 320

void function fireResize(width = sizeSmall) {
 window.innerWidth = width
 window.dispatchEvent(new Event('resize'))
}()

function EffecfulComponent() {
 const breakpoint = useBreakpoints()
 return <span>{breakpoint}</span>
}

test('useBreakpoints listen to resize window', () => {
 const { container } = render(<EffecfulComponent />)
 const span = container.firstChild
expect(span.textContent).toBe(sizeSmall.toString())
})

Переваги

  1. Функціональні компоненти на основі хуків більш наочні за класі та мають майже всі можливості реактив компоненту.
  2. Хуки дозволяють розділити один компонент на більш дрібні функції в залежності від того, які частини пов'язані.
  3. Можна повторно використовувати логіку в компонентах та ділитись цілими колекціями хуків, наприклад через npm.js

Недоліки

  1. Немає можливості викликати хуки у компонентах на основі класу (див. правила). Тому неможливо без додаткового пласту додати хуки в поточний проект, що робить нераціональним (дорожчим) їх використання. Тому вважаю, що доцільно використовувати хуки тільки в нових проектах.

Це переваги та недоліки, виявлені на перший погляд. Вважаю, що цей перелік дещо ширший, альо в цілому переваг, як мені вбачається, більше:)

P. S. Хоч я є «адептом» Vue.js я дуже привітно ставлюся до такого кроку зі сторони реактив-спільноти — не просто тримати планку найпопулярнішої js-бібліотеки, а й робити такі зміни та ще й в кращий бік. Вважаю, що React Hooks API — це майбутнє реакту, хоча підтримка класів залишається.

В репозиторії можете подивитись код більш складного прикладу реалізації цього хуку, разом з демо та прикладом юніт-тесту .

Опубліковано: 31/01/19 @ 11:00
Розділ Різне

Рекомендуємо:

Розробка реактивних та розподілених систем з Vert.x
Токсичний HR: дії, які отруюють команду
Три основні проблеми розумних будинків і як їх можна вирішити
PM дайджест #16: розбираємося з OKR, як повернути Scrum-команді натхнення, метрики в розробці
Ще 13 консенсус-протоколів для розподілених систем. Частина 2