Мы разработали инструмент с открытым исходным кодом для написания технических руководств на основе Git. Все руководства в нашем сообществе Tuque написаны с использованием этого инструмента. Добро пожаловатьStarой
Если вы хотите быстро понять, как его использовать, добро пожаловать в нашУчебная документацияой
Эта статья была написана членами сообщества Tuque.mRcнаписано, присоединяйтесьСообщество Туке, чтобы вместе создавать замечательные бесплатные технические руководства, чтобы способствовать развитию индустрии программирования.
Если вы считаете, что мы хорошо поработали, помнитеНравится + Подписаться + КомментарийСанлиан, поощряй нас писать лучшие уроки 💪
С момента выпуска React 16.8 React Hooks с ним вызвали необратимый шторм во фронтенд-круге. React Hooks предоставляют неограниченную функциональность для функциональных компонентов и устраняют многие присущие классовым компонентам ловушки. Этот урок поможет вам быстро ознакомиться с двумя наиболее часто используемыми хуками и освоить их:useState
а такжеuseEffect
. Понимая, как его использовать, вы также можете увидеть принцип, лежащий в его основе, и, кстати, реализовать приложение для визуализации COVID-19 (новая коронная пневмония).
Добро пожаловать в этот проектРепозиторий GitHubа такжеGite-репозиторий.
Начало
Предварительные условия
Прежде чем читать этот урок, я надеюсь, что вы сделали следующие приготовления:
- Освоил основы React, такие как компоненты, JSX, состояние и т. д. Если вы этого не знаете, сначала изучите.«Время для чашки чая, начать работу с фреймворком React»
- Настройте среду Node, вы можете обратиться к«Время для чашки чая, начать работу с Node.js»
Зачем там крючки?
До появления хуков разделение труда между компонентами классов и компонентами функций было примерно таким:
- компонент классаОбеспечивает полное управление состоянием и контроль жизненного цикла, обычно используемые для выполнения сложной бизнес-логики, известной как "умные компоненты"
- функциональный компонентИсключительно из данных для представления карты, нет восприятия состояния, поэтому их обычно называют «Компонент для чайников"
Некоторые команды также разрабатывают соглашения о разработке компонентов React следующим образом:
Компоненты с состоянием не отображаются, а отображаемые компоненты не имеют состояния.
Итак, какую проблему решают хуки? Мы можем попытаться обобщить репрезентативность компонентов классаБолевые точки:
- Головная боль
this
Управление, легко ввести трудно отслеживаемые ошибки - Разделение жизненного цикла не соответствует принципу «сплоченности», например
setInterval
а такжеclearInterval
Эта тесно связанная логика разделена на разные методы жизненного цикла. - Дилемма повторного использования компонентов (совместное использование данных или повторное использование функций) от раннего Mixin доКомпоненты высшего порядка (HOC), затем кRender Propsникогда не существовало четкой, интуитивно понятной и простой в обслуживании схемы повторного использования компонентов.
Да, с запуском Hooks эти болевые точки ушли в историю!
Зачем писать эту серию руководств по хукам?
Как быстро изучить и освоить React Hooks всегда было проблемой, которая мучает многих новичков или старых игроков, и автор также обнаружил следующие головные боли в ежедневном обучении и разработке:
- Официальная документация React объясняет хуки в частичном приложении, и принцип объясняется одним махом.
- Есть много отличных статей о React Hooks, но большинство из них сосредоточено на объяснении одного или двух хуков, их сложно охватить за один раз.
- Я прочитал много методов использования и даже анализ исходного кода, но я не могу сопоставить конкретные сценарии использования и не знаю, как гибко использовать их в реальной разработке.
Если у вас такое же замешательство, я надеюсь, что эта серия статей поможет вам развеять облака и сделать Крюки вашим предпочтительным оружием. Мы проведем полный проект визуализации данных COVID-19 в сочетании с принципом анимации Hooks, чтобы вы действительно освоили React Hooks!
Честно говоря, очки знаний Крюков довольно разбросаны, как и игровые предметы в парке развлечений, сложно выбрать идеальный маршрут. Но несмотря ни на что, я надеюсь, вы получите удовольствие от следующего путешествия 😊!
Инициализировать проект
Сначала инициализируйте проект через Create React App (далее CRA):
npx create-react-app covid-19-with-hooks
Немного подождав, войдите в проект.
намекать
Все наши данные взяты изNovelCOVID 19 API, вы можете щелкнуть, чтобы получить доступ к полной документации API.
Все готово, поехали!
USESTATE + USEFFECT: Newbie
Во-первых, давайте начнем с двух наиболее часто используемых хуков:useState
а такжеuseEffect
. Очень вероятно, что вы уже прикоснулись к нему и использовали его в своем обычном обучении и развитии (конечно, не беда, если вы только начинаете учиться). Но перед этим давайте познакомимся с тем, как работают функциональные компоненты React.
Понять процесс работы функциональных компонентов
Мы знаем, ХуксРаботает только с функциональными компонентами React. Поэтому понимание работы функциональных компонентов имеет решающее значение для понимания многих важных функций хуков. См. следующий рисунок:
Как видите, функциональные компоненты строго следуютUI = render(data)
режим. Когда мы вызываем функцию компонента в первый раз, запускаемпервый рендер; затем сprops
изменения, функция компонента будет вызвана снова, вызываяперерисовать.
Вам может быть интересно, почему в анимации три одинаковых компонента нарисованы рядом? Потому что я хочу визуализировать важную идею функциональных компонентов таким образом:
Каждый рендер полностью независим.
Позже мы будем следовать этому стилю и шаг за шагом рассмотрим роль хуков в функциональных компонентах.
Анализ состояния использования
Сначала давайте просто понятьuseState
Использование хуков описано в официальной документации следующим образом:
const [state, setState] = useState(initialValue);
вstate
является переменной состояния,setState
является функцией Setter, которая изменяет состояние, иinitialValue
является начальным значением состояния.
Просто смотреть на код может быть немного абстрактно, см. анимацию ниже:
По сравнению с предыдущими чисто функциональными компонентами мы вводимuseState
Этот крюк сломался за мгновение доUI = render(data)
Спокойная картина - функциональные компоненты могут на самом делеСостояние «ловушки» и функции изменения состояния извне компонента! И внимательно посмотрите на анимацию выше,Вызывая функцию Setter, вы также можете напрямую инициировать повторную визуализацию компонента.!
намекать
Вы могли заметить, что все «крючки» указывают на зеленый вопросительный знак, ниже мы подробно разберем, что это было, а теперь посмотрим, как временное дополнение к компонентам может получить доступ к «загадочному полю».
Комбинируя анимацию выше, мы можем сделать важный вывод:Каждый рендер имеет независимое значение состояния(Ведь каждый рендеринг полностью независим). То есть в каждой функцииstate
Переменная — это простопостоянный, константа, полученная из хука каждый раз, когда он отображается, и нет никакой магии, такой как привязка данных.
Это старая поговоркаCapture Valueхарактеристика. Взгляните на следующий классический код счетчика (из книги Дэнаэта замечательная статья):
function Counter() {
const [count, setCount] = useState(0);
function handleAlertClick() {
setTimeout(() => {
alert('You clicked on: ' + count);
}, 3000);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
<button onClick={handleAlertClick}>
Show alert
</button>
</div>
);
}
После реализации вышеуказанного счетчика (вы также можете напрямую передать этоSandboxопыта), выполните следующие действия: 1) Нажмите кнопку «Нажми меня», чтобы увеличить число до 3; 2) Нажмите кнопку «Показать оповещение»; 3) ВsetTimeout
Щелкните Щелкнуть меня перед срабатыванием, чтобы увеличить число до 5.
Результатом является предупреждение, показывающее 3!
Если вы считаете этот результат нормальным, поздравляем, вы поняли идею Capture Value! Если вы думаете, что это странно... давайте кратко объясним:
- Каждый рендеринг независим друг от друга, поэтому состояние, обработчики событий и т. д. в компоненте независимы каждый раз, когда он рендерится, илипринадлежат толькотот рендеринг
- мы в
count
Сработало, когда было 3handleAlertClick
функция, эта функциявспомнилcount
также 3 - Через три секунды функция только что
setTimeout
конец, выходвспомнил тогдаРезультат: 3
Это похоже на открытие дневника десятилетней давности: хотя вы открываете его сейчас, в нем все равно записано время десятилетней давности. Другими словами, дневник запечатлел это прекрасное воспоминание.
анализ использования useEffect
вы, возможно, слышалиuseEffect
Подобно методам жизненного цикла в компонентах класса. Но начинаю учитьсяuseEffect
Прежде рекомендуется на время забыть о модели жизненного цикла, ведь компоненты-функции и компоненты-классы — это разные миры. Знакомство с официальными документамиuseEffect
используется следующим образом:
useEffect(effectFn, deps)
effectFn
это реализация, которая может иметьпобочный эффектФункции эффектов (такие как сбор данных, установка/уничтожение таймеров и т. д.), которые могут возвращатьфункция очистки(уборка), такая как всем знакомаяsetInterval
а такжеclearInterval
:
useEffect(() => {
const intervalId = setInterval(doSomething(), 1000);
return () => clearInterval(intervalId);
});
Как видите, мы передаем тело функции EffectsetInterval
Запускается таймер, а затем возвращается функция очистки, чтобы уничтожить только что созданный таймер.
Хорошо, это все еще звучит абстрактно, давайте посмотрим на анимацию ниже:
Анимация имеет следующие моменты, на которые следует обратить внимание:
- Каждый Эффект должен выполняться после рендеринга, чтобы он не блокировал рендеринг и повышал производительность.
- Перед запуском каждого эффекта запустите функцию очистки эффекта предыдущего рендера (если есть).
- Когда компонент уничтожен, запустите функцию очистки последнего эффекта.
намекать
Отсрочка выполнения Эффекта до завершения рендеринга по соображениям производительности.Если вы хотите выполнить некоторую логику перед рендерингом (за счет производительности рендеринга), вы можете использовать
useLayoutEffect
крючки, используйте те жеuseEffect
Точно такие же, только сроки исполнения разные.
посмотри сноваuseEffect
Второй параметр:deps
(в зависимости от массива). Как видно из демонстрационной анимации выше, React будетЗапускайте эффект после каждого рендера. Массив зависимостей используется для управления тем, следует ли запускать эффект, что может сократить ненужные вычисления и оптимизировать производительность. В частности, пока каждый элемент в массиве зависимостей не изменился с момента последнего рендеринга, пропустите выполнение этого эффекта.
Подумав хорошенько, находимuseEffect
По сравнению с жизненным циклом компонентов предыдущего класса хуки имеют две примечательные характеристики:
- рендерится с первого раза(
componentDidMount
), перерисовать (componentDidUpdate
) и уничтожить (componentDidUnmount
) Логика трех этапов решается единым API - Поместите всю связанную логику в Эффект (например,
setInterval
а такжеclearInterval
), подчеркивая связность логики
В самом крайнем случае можно указатьdeps
пустой массив[]
, что гарантирует, что Эффектбудет выполняться только после первоначального рендеринга компонента. Фактическая анимация эффекта выглядит следующим образом:
Как видите, все последующие повторные рендеры не будут запускать выполнение Эффекта, при уничтожении компонента запускается функция Очистки Эффекта.
Уведомление
Если вы знакомы с механизмом повторного рендеринга React, вы должны догадаться.
deps
Массивы также используются при оценке того, изменился ли элемент.Object.is
Сравнивать. Поэтому скрытая опасность заключается в том, что когдаdeps
Когда один из элементов не является примитивным типом (например, функция, объект и т. д.),меняется каждый рендер, тем самым потерявdeps
Значение самого себя (условное срабатывание Эффекта). Далее мы объясним, как обойти эту дилемму.
настоящий бой
Хорошо, когда дело доходит до реального боя, мы сначала реализуем сбор глобального обзора данных (повторный сбор каждые 5 секунд). Создайтеsrc/components/GlobalStats.js
Компонент, используемый для отображения глобального обзора данных, имеет следующий код:
import React from "react";
function Stat({ number, color }) {
return <span style={{ color: color, fontWeight: "bold" }}>{number}</span>;
}
function GlobalStats({ stats }) {
const { cases, deaths, recovered, active, updated } = stats;
return (
<div className='global-stats'>
<small>Updated on {new Date(updated).toLocaleString()}</small>
<table>
<tr>
<td>
Cases: <Stat number={cases} color='red' />
</td>
<td>
Deaths: <Stat number={deaths} color='gray' />
</td>
<td>
Recovered: <Stat number={recovered} color='green' />
</td>
<td>
Active: <Stat number={active} color='orange' />
</td>
</tr>
</table>
</div>
);
}
export default GlobalStats;
можно увидеть,GlobalStats
Простой функциональный компонент без всяких хуков.
затем изменитьsrc/App.js
, импортируя только что созданныйGlobalStats
компонент, код выглядит следующим образом:
import React, { useState, useEffect } from "react";
import "./App.css";
import GlobalStats from "./components/GlobalStats";
const BASE_URL = "https://corona.lmao.ninja/v2";
function App() {
const [globalStats, setGlobalStats] = useState({});
useEffect(() => {
const fetchGlobalStats = async () => {
const response = await fetch(`${BASE_URL}/all`);
const data = await response.json();
setGlobalStats(data);
};
fetchGlobalStats();
const intervalId = setInterval(fetchGlobalStats, 5000);
return () => clearInterval(intervalId);
}, []);
return (
<div className='App'>
<h1>COVID-19</h1>
<GlobalStats stats={globalStats} />
</div>
);
}
export default App;
Как видите, мыApp
компонент, первый проходuseState
крючок введенglobalStats
Переменные состояния и функции, изменяющие это состояние. затем пройтиuseEffect
Крюк для получения данных API, есть следующие точки для примечания:
- Мы определяем
fetchGlobalStats
Асинхронная функция и вызов для получения данных вместо прямого использования этой асинхронной функции какuseEffect
первый параметр; - Создан интервал для обновления данных каждые 5 секунд;
- Возвращает функцию для интервала, созданную ранее уничтоженной.
Кроме того, второй параметр (массив зависимостей) является пустым массивом, поэтому вся функция Effect будет выполняться только один раз.
Уведомление
Иногда вы можете случайно написать Effect как асинхронную функцию:
useEffect(async () => { const response = await fetch('...'); // ... }, []);
Это нормально?Настоятельно не рекомендую этого делать.
useEffect
По соглашению функция Effect либо не возвращает никакого значения, либо возвращает функцию очистки. И здесь асинхронная функция неявно вернет Promise, что напрямую нарушает это соглашение и приведет к непредсказуемым результатам.
Наконец, прикрепите глобальный файл CSS приложения, код выглядит следующим образом (просто скопируйте и вставьте):
.App {
width: 1200px;
margin: auto;
text-align: center;
}
.history-group {
display: flex;
justify-content: center;
width: 1200px;
margin: auto;
}
table,
th,
td {
border: 1px solid #ccc;
border-collapse: collapse;
}
th,
td {
padding: 5px;
text-align: left;
}
.global-stats > table {
margin: auto;
margin-top: 0.5rem;
margin-bottom: 1rem;
}
пройти черезnpm start
Откройте проект:
Кроме того, вы можете проверить вкладку «Сеть» консоли, и вы должны увидеть, что наше приложение каждые пять секунд отправляет запрос на получение последних данных. Поздравляем, вы успешно выполнили сбор данных с помощью Hooks!
useState + useEffect: становится лучше
На предыдущем шаге мы былиApp
Состояние и эффект определены в компоненте, но фактическое приложение не может быть таким простым.Как правило, требуется несколько состояний и эффектов.Как понять и использовать это в настоящее время?
Углубленный характер умирания
В анимации в предыдущем разделе мы видели, что каждый раз, когда компонент визуализируется, мы можем «зацепить» состояние с помощью волшебного крючка, но у нас есть знак вопроса, откуда берутся эти крючки. Теперь пришло время разгадать тайну.
Уведомление
Следующие анимации не совсем соответствуют исходному коду реализации React Hooks, но это хороший способ помочь вам понять, как это работает. Конечно, это также может помочь вам съесть настоящий исходный код.
Давайте сначала посмотрим, что происходит при первом рендеринге (монтировании) компонента:
Обратите внимание на следующие моменты:
- На первом рендере мы передаем
useState
Определено несколько состояний; - каждый звонок
useState
, создаст запись Hook вне компонента, включая значение состояния (сuseState
инициализация с заданным начальным значением) и функция Setter, изменяющая состояние; - несколько вызовов
useState
Сгенерированная запись Hook формируетсвязанный список; - вызывать
onClick
функция обратного вызова, вызовsetS2
модификация функцииs2
, не только изменяет значение состояния в записи ловушки, но ивызвать повторный рендеринг.
ОК, время рендеринга, анимация выглядит следующим образом:
Видно, что связанный список записей Hook все еще существует после первоначального рендеринга и до повторного рендеринга. когда мы звоним один за другимuseState
когда,useState
Он возвращает состояние, хранящееся в связанном списке Hook, и сеттер, который изменяет состояние.
намекать
Когда вы полностью разберетесь в двух приведенных выше анимациях, вы сможете понять, почему этот хук называется
useState
вместоcreateState
Да почему так называетсяuse
, поскольку он создается, когда недоступен (при первом рендеринге), а иногда читается напрямую (при повторном рендеринге).
Проведя приведенный выше анализ, мы можем легко найти, чтоuseState
Изысканность в дизайне (от Чжан Лили:Некоторые мысли о React Hooks):
- Состояние и сеттер-функция, модифицирующая состояние, парные, причем последняя должна воздействовать на первую, на первую влияет только вторая, и в целом на них совершенно не влияет внешний мир
- Поощряет мелкозернистое и плоское определение состояния и контроль, которые имеют большое значение для предсказуемости и тестируемости поведения кода.
- Кроме
useState
(и другие хуки), функциональные компоненты по-прежнему являются «чистыми» компонентами, которые реализуют логику рендеринга, а управление состоянием инкапсулировано хуками.
Глубокое погружение в суть useEffect
справаuseState
После волны глубоких раскопок давайте раскроемuseEffect
Завеса тайны. На самом деле, как вы уже догадались, все хуки также записываются через связанный список, см. следующую демонстрацию:
Обратите внимание на некоторые из этих деталей:
-
useState
а такжеuseEffect
добавляется в список крючков каждый раз, когда он называется; -
useEffect
Дополнительно добавит функцию Эффекта, ожидающую выполнения в очереди; - После завершения рендеринга каждая функция Effect в очереди Effect вызывается по очереди.
К этому моменту так же выявился жизненный опыт двух "вопросительных знаков" в анимации предыдущего раздела - простосвязанный списокВот и все! Оглядываясь назад, мы вспоминаем момент, подчеркнутый в официальной документации React Rules of Hooks:
Вызовите хуки только на верхнем уровне.
В частности, не в цикле вложенные условные операторы используют ловушку, потому что эти динамические операторы могут привести к вызовам функций компонента ловушки каждого порядка выполнения, которые не могут быть точно такими же, что приводит к сбою ловушки списка записей данных. Конкретная сцена не нарисована анимационной материей, мозг делает ее самостоятельно ~
Не лгите: дело в Депсе
useEffect
(включая другие подобныеuseCallback
а такжеuseMemo
и т. д.) имеют массив зависимостей (deps
), интересная особенность этого параметра заключается в том, что решение об указании зависимостей полностью зависит от вас. Можно конечно выбрать "солгать" и дать пустую не смотря ни на чтоdeps
Массив, как будто поговорка «Эта функция эффекта не имеет зависимостей, доверять мне».
Однако этот вид ленивого подхода, очевидно, приведет к всем видам ошибках. Вообще говоря, используетсяprop
илиstate
следует добавить кdeps
в массив. Кроме того, React официально запустил специальныйПлагин ESLint, что может помочь вам исправить это автоматическиdeps
множество (Честно говоря, иногда автоматический фикс этого плагина был совсем отстойным......).
настоящий бой
Начиная с этого шага, мы будем использоватьRechartsКак библиотека диаграмм для визуальных приложений, она обеспечивает отличный уровень связывания для D3 и React. Добавьте его с помощью следующей командыrecharts
полагаться:
npm install recharts
Создайтеsrc/components/CountriesChart.js
Используется для демонстрации гистограмм связанных данных в нескольких странах. Код выглядит следующим образом:
import React from "react";
import {
BarChart,
CartesianGrid,
XAxis,
YAxis,
Tooltip,
Legend,
Bar,
} from "recharts";
function CountriesChart({ data, dataKey }) {
return (
<BarChart
width={1200}
height={250}
style={{ margin: "auto" }}
margin={{ top: 30, left: 20, right: 30 }}
data={data}
>
<CartesianGrid strokeDasharray='3 3' />
<XAxis dataKey='country' />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey={dataKey} fill='#8884d8' />
</BarChart>
);
}
export default CountriesChart;
Создайтеsrc/components/SelectDataKey.js
, используемый для выбора отображаемых ключевых индикаторов, код выглядит следующим образом:
import React from "react";
function SelectDataKey({ onChange }) {
return (
<>
<label htmlFor='key-select'>Select a key for sorting: </label>
<select id='key-select' onChange={onChange}>
<option value='cases'>Cases</option>
<option value='todayCases'>Today Cases</option>
<option value='deaths'>Death</option>
<option value='recovered'>Recovered</option>
<option value='active'>Active</option>
</select>
</>
);
}
export default SelectDataKey;
SelectDataKey
Используется, чтобы позволить пользователям выбирать следующие ключевые показатели:
-
cases
: Совокупное количество подтвержденных случаев -
todayCases
: Подтвержденные случаи сегодня -
deaths
: Совокупное количество смертей -
recovered
: Количество вылеченных -
active
: Количество существующей диагностики
Наконец мы в корневом компонентеsrc/App.js
Введите два компонента, созданные выше, код выглядит следующим образом:
// ...
import GlobalStats from "./components/GlobalStats";
import CountriesChart from "./components/CountriesChart";
import SelectDataKey from "./components/SelectDataKey";
const BASE_URL = "https://corona.lmao.ninja/v2";
function App() {
const [globalStats, setGlobalStats] = useState({});
const [countries, setCountries] = useState([]);
const [key, setKey] = useState("cases");
useEffect(() => {
// ...
}, []);
useEffect(() => {
const fetchCountries = async () => {
const response = await fetch(`${BASE_URL}/countries?sort=${key}`);
const data = await response.json();
setCountries(data.slice(0, 10));
};
fetchCountries();
}, [key]);
return (
<div className='App'>
<h1>COVID-19</h1>
<GlobalStats stats={globalStats} />
<SelectDataKey onChange={(e) => setKey(e.target.value)} />
<CountriesChart data={countries} dataKey={key} />
</div>
);
}
export default App;
можно увидеть:
- Создаем два новых состояния
countries
(данные по всем странам) иkey
(Индикаторы для сортировки данных - пять выше); - Мы прошли
useEffect
Хук для сбора данных аналогичен предыдущему сбору глобальных данных, но обратите внимание, что второй параметр (массив зависимостей) на нашей стороне равен[key]
, то есть только тогда, когдаkey
Когда состояние изменится, оно будет вызваноuseEffect
функция внутри. - Наконец, используйте два созданных ранее подкомпонента и передайте соответствующие данные и функции обратного вызова.
Запустив проект, вы увидите, что гистограмма показывает данные первой десятки стран, и вы можете изменить показатели сортировки (например, вы можете начать с кумулятивного диагноза по умолчанию).cases
переключиться на список погибшихdeaths
):
Это выглядит очень хорошо!
На этом первая часть этой серии закончена, я надеюсь, вы действительно понимаетеuseState
а такжеuseEffect
- Два наиболее часто используемых хука. существуетСледующий урок, мы продолжим работу с пользовательскими хуками иuseCallback
.
использованная литература
- Реагировать на официальную документацию
- Робин Верух:How to fetch data with React Hooks?
- Дэн Абрамов:A Complete Guide to useEffect
- Дэн Абрамов:How Are Function Components Different from Classes?
- Руди Ярдли:React hooks: not magic, just arrays
- Усадьба Эйтан:Под капотом системы хуков React
- Ян Лян:Полное руководство для начинающих по React Hooks
- Чжан Лили:Некоторые мысли о React Hooks
Хотите узнать больше интересных практических технических руководств? ПриходитьСообщество ТукеМагазин вокруг.