Гачки - блискуче доповнення React. Вони спрощують багато логіки, яку раніше доводилося розділяти на різні життєві цикли з class
компонентами.
Однак вони вимагають іншої ментальної моделі, особливо для першокласників.
Я також записав короткий відеосюжет за цією статтею, який вам може бути корисним.
Розряд і дросель
Існує маса публікацій у блозі, написаних про розрив і дросель, тому я не буду занурюватися в те, як писати свій власний розвід і дросель. Для стислості розглянемо debounce
і throttle
від Lodash.
Якщо вам потрібно швидке оновлення, обидва приймають функцію (зворотного виклику) і затримку в мілісекундах (скажімо x
), а потім обидва повертають іншу функцію з якоюсь особливою поведінкою:
debounce
: Повертає функцію , яку можна назвати будь-яке число раз (можливо , в швидких сукцесіях) , але буде викликати тільки функцію зворотного виклику після очікування вx
мс від останнього виклику.throttle
: повертає функцію, яку можна викликати будь-яку кількість разів (можливо, швидко), але вона буде викликати зворотний виклик не більше одного разу на кожнуx
мс.
Використовуйте
У нас є мінімальний редактор блогу (ось репо GitHub), і ми хотіли б зберегти допис у блозі через 1 секунду після того, як користувач припинить друкувати.
Ви також можете звернутися до цього Codesandbox, якщо хочете побачити остаточну версію коду.
Мінімальна версія нашого редактора виглядає так:
import React, { useState } from 'react'; import debounce from 'lodash.debounce'; function App() { const [value, setValue] = useState(''); const [dbValue, saveToDb] = useState(''); // would be an API call normally const handleChange = event => { setValue(event.target.value); }; return ( Blog
Editor (Client)
{value} Saved (DB)
{dbValue} ); }
Тут saveToDb
насправді був би виклик API до серверної бази. Щоб зробити речі простішими, я зберігаю їх у стані, а потім передаю як dbValue
.
Оскільки ми хочемо виконати цю операцію збереження лише після того, як користувач перестане вводити текст (через 1 секунду), це слід скасувати .
Ось репо та гілка початкового коду.
Створення розв'язаної функції
Перш за все, нам потрібна розв'язана функція, яка обертає виклик saveToDb
:
import React, { useState } from 'react'; import debounce from 'lodash.debounce'; function App() { const [value, setValue] = useState(''); const [dbValue, saveToDb] = useState(''); // would be an API call normally const handleChange = event => { const { value: nextValue } = event.target; setValue(nextValue); // highlight-starts const debouncedSave = debounce(() => saveToDb(nextValue), 1000); debouncedSave(); // highlight-ends }; return {/* Same as before */}; }
Але це насправді не працює, оскільки функція debouncedSave
створюється свіжо при кожному handleChange
виклику. Це призведе до зняття кожного натискання клавіші, а не до зняття всього вхідного значення.
useCallback
useCallback
зазвичай використовується для оптимізації продуктивності при передачі зворотних викликів дочірнім компонентам. Але ми можемо використовувати його обмеження запам'ятовування функції зворотного виклику, щоб забезпечити debouncedSave
посиланням ту саму розірвану функцію в рендерах.
Я також написав цю статтю тут на freeCodeCamp, якщо ви хочете зрозуміти основи мемоїзації.
Це працює як слід:
import React, { useState, useCallback } from 'react'; import debounce from 'lodash.debounce'; function App() { const [value, setValue] = useState(''); const [dbValue, saveToDb] = useState(''); // would be an API call normally // highlight-starts const debouncedSave = useCallback( debounce(nextValue => saveToDb(nextValue), 1000), [], // will be created only once initially ); // highlight-ends const handleChange = event => { const { value: nextValue } = event.target; setValue(nextValue); // Even though handleChange is created on each render and executed // it references the same debouncedSave that was created initially debouncedSave(nextValue); }; return {/* Same as before */}; }
useRef
useRef
дає нам змінний об'єкт, current
властивість якого відноситься до переданого початкового значення. Якщо ми не змінимо його вручну, значення зберігатиметься протягом усього життя компонента.
Це схоже на властивості екземпляра класу (тобто визначення методів та властивостей на this
).
Це також працює як слід:
import React, { useState, useRef } from 'react'; import debounce from 'lodash.debounce'; function App() { const [value, setValue] = useState(''); const [dbValue, saveToDb] = useState(''); // would be an API call normally // This remains same across renders // highlight-starts const debouncedSave = useRef(debounce(nextValue => saveToDb(nextValue), 1000)) .current; // highlight-ends const handleChange = event => { const { value: nextValue } = event.target; setValue(nextValue); // Even though handleChange is created on each render and executed // it references the same debouncedSave that was created initially debouncedSave(nextValue); }; return {/* Same as before */}; }
Продовжуйте читати в моєму блозі, як абстрагувати ці поняття у власні гачки або переглянути відеосерію.
Ви також можете стежити за мною у Twitter, щоб залишатися в курсі останніх публікацій. Сподіваюсь, ця публікація вам знайшла користь. :)