Хуки появились в React 16.8 впервые. Это позволяет вам использовать состояние и другие функции React без написания классов.
Из этого предложения на официальном сайте мы можем ясно понять, чтоHook
Добавлены функциональные компонентыstate
Использование , в прошлом, функциональные компоненты не могли иметь своего состояния, только черезprops
так же какcontext
сделать свой собственныйUI
, а в бизнес-логике некоторые сценарии должны использоватьstate
, то мы можем определить функциональные компоненты только какclass
компоненты. а теперь черезHook
, мы можем легко поддерживать наше состояние в функциональных компонентах, не меняя их наclass
компоненты.
React Hooks
Проблема, которую необходимо решить, — это совместное использование состояния.Под общим состоянием здесь понимается только повторное использование логики состояния, а не совместное использование данных. мы знаем, что вReact Hooks
Раньше для решения проблемы повторного использования логики состояния мы обычно использовалиhigher-order components
а такжеrender-props
, то если есть оба решения, то почемуReact
Разработчики также должны представитьReact Hook
? дляhigher-order components
а такжеrender-props
,React Hook
Где преимущества?
Пример хука реакции
Давайте сначала посмотримReact
официально даноReact Hook
изdemo
import { useState } from 'React';
function Example() {
// Declare a new state variable, which we'll call "count"
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Посмотрим еще разReact Hook
Если да, то как добиться
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>You clicked {this.state.count} times</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
</div>
);
}
}
Видно, что вReact Hook
середина,class Example
Компонент становится функциональным компонентом, но функциональный компонент имеет свое собственное состояние и также может обновлять свое состояние. Все это благодаряuseState
этоHook
,useState
вернет пару значений: текущее состояние и функцию, позволяющую обновить его, которую можно вызвать в обработчике событий или в другом месте. это что-то вродеclass
компонентthis.setState
, но новую не поставитstate
и старыйstate
объединить
React
Решения для повторного использования логики состояния
Hook
это еще одно решение для повторного использования логики состояния,React
Разработчики постоянно предлагали и улучшали схему повторного использования логики состояния, начиная сMixin
к компонентам более высокого порядка, чтобыRender Props
до сих порHook
, давайте кратко рассмотрим предыдущее решение
Mixin
модель
существуетReact
На начальном этапе было предложеноMixin
Шаблон для повторного использования логики между компонентами. существуетJavascript
, мы можем поставитьMixin
Наследование рассматривается как способ расширения функциональности коллекции.Каждый новый объект, который мы определяем, имеет прототип, от которого он может наследовать дополнительные свойства.Прототипы могут быть унаследованы от других объектов, но что более важно, возможность определять свойства для любого количества объектов.Мы может воспользоваться этим фактом для облегчения функционального повторного использования.
React
серединаmixin
В основном используется в двух совершенно не связанных компонентах, есть набор в принципе схожих функций, мы можем извлечь его черезmixin
способ ввода, чтобы добиться повторного использования кода. Например, в разных компонентах компоненты нужно обновлять время от времени, мы можем создатьsetInterval()
для достижения этой функции, и когда компонент будет уничтожен, нам нужно выгрузить эту функцию. Таким образом, можно создать простойmixin
, предоставляя простойsetInterval()
функция, которая автоматически очищается при уничтожении компонента.
var SetIntervalMixin = {
componentWillMount: function() {
this.intervals = [];
},
setInterval: function() {
this.intervals.push(setInterval.apply(null, arguments));
},
componentWillUnmount: function() {
this.intervals.forEach(clearInterval);
}
};
var createReactClass = require('create-React-class');
var TickTock = createReactClass({
mixins: [SetIntervalMixin], // 使用 mixin
getInitialState: function() {
return {seconds: 0};
},
componentDidMount: function() {
this.setInterval(this.tick, 1000); // 调用 mixin 上的方法
},
tick: function() {
this.setState({seconds: this.state.seconds + 1});
},
render: function() {
return (
<p>
React has been running for {this.state.seconds} seconds.
</p>
);
}
});
ReactDOM.render(
<TickTock />,
document.getElementById('example')
);
mixin
Недостатки
- разные
mixin
Может быть взаимозависимым, и связь слишком сильная, что приводит к высоким затратам на техническое обслуживание в более поздний период. -
mixin
Имена в списке могут конфликтовать и не могут использовать одно и то же имя.mixin
-
mixin
Даже если они начинаются с простого, со временем они могут усложняться лавинообразно, поскольку бизнес-сценарии умножаются.
Конкретные недостатки смотрите по этой ссылкеМиксины - это бич
потому чтоmixin
из этих недостатков существуют, вReact
устарело вmixin
режим повторного использования кода,React
Вместо этого полностью рекомендуется использовать компоненты более высокого порядка.mixin
режим, покаES6
сама не содержитmixin
служба поддержки. Поэтому, когда выReact
используется вES6 class
не будет поддерживатьmixins
.
компоненты более высокого порядка
компоненты более высокого порядка
(HOC)
даReact
Усовершенствованный метод повторного использования логики компонентов в .HOC
не самReact API
частьReact
Шаблоны проектирования, сформированные путем объединения характеристик
Расширенные компоненты неReact
который предоставилAPI
, ноReact
Технику использования компонентов более высокого порядка можно рассматривать как шаблон декоратора (Decorator Pattern
)существуетReact
реализация. Шаблон декоратора: для динамического назначения обязанностей объектам и расширения функциональности декораторы предоставляют более гибкую альтернативу наследованию.
В частности, компонент более высокого порядка — это функция, параметр которой является компонентом, а возвращаемое значение — новым компонентом.
Компонент преобразует свойства в пользовательский интерфейс, а компонент более высокого порядка преобразует компонент в другой компонент.
Мы можем динамически добавлять функции печати журнала к другим компонентам через компоненты более высокого порядка, не затрагивая функции исходных компонентов.
function logProps(WrappedComponent) {
return class extends React.Component {
componentWillReceiveProps(nextProps) {
console.log('Current props: ', this.props);
console.log('Next props: ', nextProps);
}
render() {
return <WrappedComponent {...this.props} />;
}
}
}
Render Propss
Термин «реквизиты рендеринга» относится к простой технике совместного использования кода между компонентами React с использованием реквизита, значением которого является функция.
Компоненты с Render Props принимают функцию, которая возвращает элемент React, и вызывают ее вместо реализации собственной логики рендеринга.
Ниже мы предоставляемprop
из<Mouse>
компоненты, которые могут динамически решать, что нужно визуализировать, чтобы<Mouse>
Логика компонента и повторное использование состояния без изменения структуры его рендеринга.
class Cat extends React.Component {
render() {
const mouse = this.props.mouse;
return (
<img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
);
}
}
class Mouse extends React.Component {
constructor(props) {
super(props);
this.handleMouseMove = this.handleMouseMove.bind(this);
this.state = { x: 0, y: 0 };
}
handleMouseMove(event) {
this.setState({
x: event.clientX,
y: event.clientY
});
}
render() {
return (
<div style={{ height: '100%' }} onMouseMove={this.handleMouseMove}>
{this.props.render(this.state)}
</div>
);
}
}
class MouseTracker extends React.Component {
render() {
return (
<div>
<h1>移动鼠标!</h1>
<Mouse render={mouse => (
)}/>
</div>
);
}
}
Однако обычно мы говоримRender Props
Это потому, что шаблон называетсяRender Props
, а не потому, что его нужно использоватьrender
правильноprop
Назови это. Мы также можем выразить это
<Mouse>
{mouse => (
<Cat mouse={mouse} />
)}
</Mouse>
Мотивация React Hook
React Hook
Это еще одно совершенно новое решение, предложенное официальным сайтом.React Hook
Прежде давайте посмотримReact Hook
мотивация
- Повторное использование логики состояния между компонентами сложно
- Сложные компоненты становятся трудными для понимания
- Трудно понять
class
Вот мое понимание этих трех мотивов:
Повторное использование логики состояния между компонентами сложно, прежде чем мы передадим компонент более высокого порядка (Higher-Order Components
) и свойства рендеринга (Render Propss
) для решения сложной проблемы повторного использования логики состояния. Многие библиотеки используют эти шаблоны для повторного использования логики состояния, такой как наша обычно используемаяredux
,React
Router
. Компоненты более высокого порядка и свойства рендеринга являются вложенными и совместно используемыми компонентами слой за слоем посредством комбинирования, что значительно увеличит иерархическую взаимосвязь нашего кода, что приведет к чрезмерно преувеличенной вложенности. отReact
изdevtool
Мы можем ясно видеть уровень вложенности, вызванный использованием этих двух режимов.
Сложные компоненты становятся трудными для понимания, В постоянно меняющихся бизнес-требованиях компоненты будут постепенно заполняться логикой состояния и побочными эффектами, и каждый жизненный цикл часто содержит некоторую неуместную логику. Мы обычно пишем код по единому принципу функций, функция обычно обрабатывает только одну вещь, но в функции-хуке жизненного цикла она обычно делает много вещей одновременно. Например, когда нам нужноcomponentDidMount
инициировано вajax
В этом жизненном цикле прописывается запрос на получение данных, а иногда и привязка события, и даже иногда нужноcomponentWillReceiveProps
Данные следуютcomponentDidMount
Такая же обработка.
Код, который связан друг с другом и нуждается в изменении, разделяется, а код, который совершенно не связан, объединяется в одном методе. Это чревато ошибками и приводит к несоответствиям в логике.
непонятный класс, лично чувствую, используяclass
Компоненты все еще в порядке, если вы понимаетеclass
изthis
Указав на проблему привязки, на самом деле начать работу несложно. Поймите, что это неReact
своеобразное поведение; это на самом деле связано сКак работают функции JavaScriptСвязанный. Так что просто поймиJS
Как работает функция, на самом делеthis
Привязка ни о чем. просто иногда для гарантииthis
Дело верное, мы обычно пишем много кода для привязкиthis
Если вы забудете привязать, будет разныеbug
. связыватьthis
метод:
1.this.handleClick = this.handleClick.bind(this);
2.<button onClick={(e) => this.handleClick(e)}>
Click me
</button>
Поэтому для решения вышеуказанных проблемReact Hook
был воспитан
использование хука состояния
Давайте вернемся к коду и посмотрим, как определить его в функциональном компоненте.state
import React, { useState } from 'React';
const [count, setCount] = useState(0);
-
useState
что ты сделалМы видим, что в этой функции мы передаем
useState
определяет «переменную состояния», которая связана сclass
внутриthis.state
обеспечивает точно такую же функциональность. Эквивалентно следующему кодуclass Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; }
-
useState
параметрВ коде мы передаем
0
так какuseState
параметр, значение этого параметра будет рассматриваться какcount
Первоначальный значение. Конечно, этот параметр не ограничивается передачей чисел и строк, вы можете передать объект как начальный.state
. еслиstate
Нужно хранить значения нескольких переменных, а затем вызывать несколько разuseState
Только что -
useState
возвращаемое значениеВозвращаемое значение: текущее
state
и обновитьstate
функция, аналогичнаяclass
вthis.state.count
а такжеthis.setState
Аналогично, с той лишь разницей, что вам нужно получить их парами. Видеть[count, setCount]
Легко понять, что это метод записи деконструированного массива ES6. Эквивалентно следующему кодуlet _useState = useState(0);// 返回一个有两个元素的数组 let count = _useState[0];// 数组里的第一个值 let setCount = _useState[1];// 数组里的第二个值
Чтение значения состояния
Просто используйте переменные
предыдущее написание
<p>You clicked {this.state.count} times</p>
написать сейчас
<p>You clicked {count} times</p>
обновить состояние
пройти черезsetCount
обновление функции
предыдущее написание
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Click me
</button>
написать сейчас
<button onClick={() => setCount(count + 1)}>
Click me
</button>
здесьsetCount
Полученный параметр представляет собой измененное новое значение состояния.
Объявить несколько переменных состояния
Мы можем использовать несколько раз в компонентеstate Hook
объявить несколькоstate
Переменная
function ExampleWithManyStates() {
// 声明多个 state 变量!
const [age, setAge] = useState(42);
const [fruit, setFruit] = useState('banana');
const [todos, setTodos] = useState([{ text: 'Learn Hooks' }]);
// ...
}
Реакция предполагает, что когда вы звонили несколько разuseState
, вы можете гарантировать, что порядок, в котором они называются, одинаково каждый раз, когда вы визуете
ЗачемReact
Понятно, что порядок вызова при каждом их отображении неизменен.Hook
критический вопрос
Правила хука
Hook
Суть в томJavaScript
Функция, но когда вы используете его, вам нужно следовать двум правилам. а такжеReact
Необходимо соблюдать эти два правила, иначе будут исключенияbug
- Использовать только на верхнем уровне
Hook
Не вызывайте хуки в циклах, условных выражениях или вложенных функциях,убедитесь, что вы всегдаReact
Верхний уровень функции вызывает их
- только в
React
вызов функцииHook
Не вызывайте хуки в обычных функциях JavaScript.
Причина этих двух правил в том, что мы можем использовать несколькоState Hook
илиEffect Hook
,React
полагаться наHook
порядок звонков, чтобы узнать, какойstate
что соответствуетuseState
function Form() {
const [name1, setName1] = useState('Arzh1');
const [name2, setName2] = useState('Arzh2');
const [name3, setName3] = useState('Arzh3');
// ...
}
// ------------
// 首次渲染
// ------------
useState('Arzh1') // 1. 使用 'Arzh1' 初始化变量名为 name1 的 state
useState('Arzh2') // 2. 使用 'Arzh2' 初始化变量名为 name2 的 state
useEffect('Arzh3') // 3. 使用 'Arzh3' 初始化变量名为 name3 的 state
// -------------
// 二次渲染
// -------------
useState('Arzh1') // 1. 读取变量名为 name1 的 state(参数被忽略)
useState('Arzh2') // 2. 读取变量名为 name2 的 state(参数被忽略)
useEffect('Arzh3') // 3. 读取变量名为 name3 的 state(参数被忽略)
если мы нарушимReact
правила, использующие условный рендеринг
if (name !== '') {
const [name2, setName2] = useState('Arzh2');
}
Допустим, в первый раз(name !== '')
дляtrue
, выполнить этоHook
, второй рендер(name !== '')
дляfalse
, не делайте этогоHook
,ТакHook
Порядок вызовов изменится, в результате чегоbug
useState('Arzh1') // 1. 读取变量名为 name1 的 state
//useState('Arzh2') // 2. Hook被忽略
useEffect('Arzh3') // 3. 读取变量名为 name2(之前为name3) 的 state
React
не знаю второйuseState
изHook
что надо вернуть.React
подумал бы, что в компоненте второйHook
Вызов подобен последнему рендерингу, соответствующемуarzh2
изuseState
, но не так. так вот почемуReact
ОбязательныйHook
Использование должно следовать этим двум правилам, и мы можем использоватьeslint-plugin-React-Hooks
для обеспечения соблюдения ограничений
Использование хука эффекта
Мы добавляем в приведенный выше кодEffect Hook
использование, добавление побочных эффектов к функциональным компонентам, изменение заголовка веб-страницы
useEffect(() => {
document.title = `You clicked ${count} times`;
});
Если вы знакомы с функциями жизненного цикла класса React, вы можете поместить
useEffect
Крюк какcomponentDidMount
,componentDidUpdate
а такжеcomponentWillUnmount
Сочетание этих трех функций.
То есть мы можем полностьюuseEffect
чтобы заменить эти три функции спасательного крюка
Давайте рассмотрим сценарии, которые обычно требуют побочных эффектов, таких как отправка запросов, ручные измененияdom
, ведение журнала и т. д. Обычно мы будемdom
Рендеринг завершен и последующийdom
При повторном обновлении вызовите нашу побочную операцию. Мы можем посмотреть на реализацию предыдущего жизненного цикла
componentDidMount() {
document.title = `You clicked ${this.state.count} times`;
}
componentDidUpdate() {
document.title = `You clicked ${this.state.count} times`;
}
Это то, что мы упоминали вышеReact Hook
Один из вторых проблемных источников мотивации, необходимость вызывать один и тот же код как при первом рендере, так и при последующих рендерах.
Effect
По умолчанию после первого рендерингаа такжеОн будет выполняться после каждого обновления, что избавит нас от необходимости думать о том,componentDidMount
ещеcomponentDidUpdate
при выполнении,Просто нужно понимать, что Эффект выполняется после рендеринга компонента.
явные побочные эффекты
Иногда нам нужно очистить некоторые побочные эффекты, например, если у нас есть требование опросить сервер, чтобы запросить последний статус, нам нужно очистить операцию опроса при удалении.
componentDidMount() {
this.pollingNewStatus()
}
componentWillUnmount() {
this.unPollingNewStatus()
}
мы можем использоватьEffect
чтобы устранить эти побочные эффекты, простоEffect
вернуть функцию
useEffect(() => {
pollingNewStatus()
//告诉React在每次渲染之前都先执行cleanup()
return function cleanup() {
unPollingNewStatus()
};
});
Очевидная разница в том, чтоuseEffect
На самом деле он будет выполняться перед каждым рендерингомcleanup()
,а такжеcomponentWillUnmount
будет выполняться только один раз.
Оптимизация производительности эффектов
useEffect
Фактически, он выполняется при каждом обновлении, что в некоторых случаях может вызвать проблемы с производительностью. тогда мы можем пропуститьEffect
Выполнить оптимизацию производительности. существуетclass
компонент, мы можем передатьcomponentDidUpdate
добавить пару вprevProps
или prevState
Сравнительно-логическое решение
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
document.title = `You clicked ${this.state.count} times`;
}
}
существуетEffect
, мы можем добавитьEffect
Второй параметр , если нет изменений, пропустить обновление
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
Другие крючки
Из-за недостатка места он не будет здесь расширяться, если вам интересно, вы можете просмотреть его на своем официальном сайте.