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
Базові:
- useState ;
- useEffect ;
- useContext .
Додаткові:
- useReducer ;
- useCallback ;
- useMemo ;
- useRef ;
- useImperativeHandle ;
- useLayoutEffect ;
- useDebugValue .
Напишемо кілька рядків
На основі хуків реакту напишемо власний (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(), []) ...
Іншим аргументом хук приймає масив об'єктів, що необхідно відстежувани. Такий механізм потрібен для контрольованих викликів. Тобто побічні ефекти в контексті хука будуть виконанні тільки при зміні значень, переданих у масив об'єктів.
Важливі правила
- Викликайте хуки тільки в середині функціональних компонентів.
- Викликайте хуки тільки на верхньому рівні функції, тобто на початку блоку. Не викликайте хуки в межах 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()) })
Переваги
- Функціональні компоненти на основі хуків більш наочні за класі та мають майже всі можливості реактив компоненту.
- Хуки дозволяють розділити один компонент на більш дрібні функції в залежності від того, які частини пов'язані.
- Можна повторно використовувати логіку в компонентах та ділитись цілими колекціями хуків, наприклад через npm.js
Недоліки
- Немає можливості викликати хуки у компонентах на основі класу (див. правила). Тому неможливо без додаткового пласту додати хуки в поточний проект, що робить нераціональним (дорожчим) їх використання. Тому вважаю, що доцільно використовувати хуки тільки в нових проектах.
Це переваги та недоліки, виявлені на перший погляд. Вважаю, що цей перелік дещо ширший, альо в цілому переваг, як мені вбачається, більше:)
P. S. Хоч я є «адептом» Vue.js я дуже привітно ставлюся до такого кроку зі сторони реактив-спільноти — не просто тримати планку найпопулярнішої js-бібліотеки, а й робити такі зміни та ще й в кращий бік. Вважаю, що React Hooks API — це майбутнє реакту, хоча підтримка класів залишається.
В репозиторії можете подивитись код більш складного прикладу реалізації цього хуку, разом з демо та прикладом юніт-тесту .
Опубліковано: 31/01/19 @ 11:00
Розділ Різне
Рекомендуємо:
Розробка реактивних та розподілених систем з Vert.x
Токсичний HR: дії, які отруюють команду
Три основні проблеми розумних будинків і як їх можна вирішити
PM дайджест #16: розбираємося з OKR, як повернути Scrum-команді натхнення, метрики в розробці
Ще 13 консенсус-протоколів для розподілених систем. Частина 2