React Hooks
Зачем нужны хуки?
Мы знаем, что односторонний поток данных и компонентизация, обеспечиваемые React, помогают нам превратить огромный проект в небольшие независимые повторно используемые компоненты. Но иногда мы не можем разделить дальшеочень сложные компоненты, потому что логика внутри нихсостояние, которые нельзя абстрагировать на функциональные компоненты. Так что иногда мы можем написать что-то совершенно неподходящее для многоразовой разработки:
- огромный компонентсложно рефакторить
- повторяющаяся логикаНеобходимо написать повторяющийся код для нескольких жизненных циклов нескольких компонентов.
- сложные шаблоны приложенийПодобно свойствам рендеринга для компонентов высшего порядка.
Но, к счастью, появление хуков позволяет нам организовать логику внутри компонента в повторно используемую.изолятор.
Задачи, которые решают хуки:
Повторное использование логики, содержащей состояние, в компонентах. Хуки могут абстрагировать логику, содержащую состояние, от компонентов, а также помочь намНе переписывать структуру компонентаВ случае мультиплексной логики. Хуки обычно используются для функциональных компонентов и недействительны в компонентах класса. Давайте разделим код на основе того, что они делают, а не жизненного цикла. Короче говоря, хуки реализуют то, что мы используем в функциональных компонентах.Переменные состоянияа такжеаналогичныйопераций в жизненном цикле.
Грамматические правила использования хуков
- Хуки можно вызывать только на верхнем уровне. Хуки не вызываются в циклах, потоке управления и вложенных функциях.
- Хуки можно вызывать только из функциональных компонентов React. Хуки не вызываются в обычных функциях JS.
Создать хуки
- использовать
useState
Создать крючок
import {useState} from 'react';
function hooks(){
// 声明一个名为 count 的新状态变量
const [count, setCount] = useState(0);
// 第二个参数 setCount 为一个可以更新状态的函数
// useState 的参数即为初始值
return (
<div>
<p>当前的状态量为: {count}</p>
<button onClick={() => setCount(count + 1)}>点击加一</button>
</div>
)
}
- использовать
useEffect
выполнить соответствующее действие
import {useState, useEffect} from 'react';
function hooks(){
const [count, setCount] = useState(0);
// 类似于 componentDidMount 和 componentDidUpdate
// 在 useEffect 中可以使用组建的 state 和 props
// 在每次渲染后都执行 useEffect
useEffect(() => {
window.alert(`You have clicked ${count} times`);
})
return (
<div>
<p>当前的状态量为: {count}</p>
<button onClick={() => setCount(count + 1)}>点击加一</button>
</div>
)
}
Крючки независимые
Мы используем один и тот же хук в двух разных компонентах, они независимы друг от друга, и даже если мы используем два хука в одном компоненте, они независимы друг от друга.
Как React гарантирует, что useState не зависит друг от друга
React на самом деле основан наuseState
Порядок появления гарантированuseState
независимы друг от друга.
// 首次渲染
const [num, setNum] = useState(1); // 将num初始化为1
const [str, setStr] = useState('string'); // 将str初始化为'string'
const [obj, setObj] = useState({id:1}); // ....
// 第二次渲染
const [num, setNum] = useState(1); // 读取状态变量num的值, 此时传入的参数已被忽略,下同
const [str, setStr] = useState('string'); // 读取状态变量str的值
const [obj, setObj] = useState({id:1}); // ....
В то же время именно потому, что порядок гарантированно независим, поэтому React предусматривает, что мы должны писать хуки в самом внешнем слое, а не в условных операторах, чтобы гарантировать согласованность порядка выполнения хуков. , нам следуетuseEffect
Запишите условие в функции
Effect Hooks
useEffect для передачи React метода, который React вызовет после обновления DOM. Обычно мы помещаем useEffect внутрь компонента, чтобы иметь прямой доступ к состоянию и свойствам. Помните, что useEffect вызывается после каждого рендера.
Эффекты, которые необходимо очистить
нам иногда нужновнешний источник данныхЧтобы получить данные, мы должны очистить эффект, чтобы избежать утечек памяти. В настоящее время нам нужно вернуть функцию в эффекте, чтобы очистить его. React будет очищать каждый раз, когда компонент касается и монтируется. Более полезный сценарий, когда мы находимся вuseEffect
Если выполняется асинхронный запрос, из-за неопределенности асинхронного времени нам нужно завершить предыдущий запрос перед выполнением следующего асинхронного запроса, поэтому нам нужно очистить.
useEffect(() => {
let canceled = false;
const getData = async () => {
const res = await fetch(api);
if(!canceled) {
// 展示 res
}
}
getData();
// return 的即为我们的清理函数
return () => {
canceled = true;
}
});
На этом этапе при повторном рендеринге мы можем избежать состояния гонки, вызванного асинхронными запросами, тем самым избегая нестабильности данных.
Настройте Эффект, который выполняется условно
мы можем датьuseEffect
Передача второго параметра приведет к повторному выполнению Эффекта только тогда, когда все значения состояния во втором параметре (массиве) изменились.
useEffect(() => {
window.alert(`you had clicked ${count} times`);
}, [count]); //只有当 count 发生变化时才会重新执行effect
Использование экземпляров в функциональных компонентах
Так как this в функциональных компонентах нет, мы не можем использовать ref, но хуки помогают нам решить эту проблему, он предоставляетuseRef
метод для создания экземпляра для нас, и входящие параметры будут смонтированы в экземпляре.current
В свойствах возвращенный экземпляр сохраняется до конца всего срока службы.
function RefExample() {
const ref1 = useRef(null);
return (
<div>
<input ref={ref1} type="text" />
<button onClick={() => {ref1.current.focus()}}
</div>
)
}
Типы крючков
Если вы хотите использовать управление состоянием типа Redux, а не перечисленные выше типы переменных состояния, OK, React также предоставляет намuseReducer
Сюда. так какuseState
В качестве альтернативы мы можем использоватьdispatch
способ изменения переменных состояния.
// 初始化的状态变量
const initState = {count:0};
// 编写 reducer 处理函数
function reducer(state, action) {
switch(action.type) {
case 'increment': return {count: state.count + 1};
case 'decrement': return {count: state.count - 1};
}
}
function counter({initState}) {
const [state, dispatch] = useReducer(reducer, initState);
return (
<div>
<p>Count: {state.count}</p>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</div>
)
}
Хуки в виде обратных вызовов
Мы можем выполнить эффект, прослушивая переменную состояния и выполняя функцию обратного вызова после преобразования.В этот момент вы можете спросить, зачем использовать так много встроенных функций с хуками, разве это не влияет на производительность? К счастью, в JavaScriptФункция закрытияСпектакль очень быстрый, нам очень помог. Есть два типа хуков в виде обратных вызовов,useCallback
а такжеuseMemo
.
Конверсионные отношения между ними:
useCallback(fn, inputs) === useMemo(() => fn, inputs)
useCallback
Как это помогает нам повысить производительность? На самом деле, он фактически кэширует экземпляр встроенной функции обратного вызова для каждого рендеринга, а затем, сопоставляется ли он сshouldComponentUpdate
илиReact.memo
Все можно добиться эффекта уменьшения ненужного рендеринга. Это также напоминает нам, чтоReact.memo
а такжеReact.useCallback
Как правило, он используется в комбинации, и без одного из них может не достичь эффекта повышения производительности.
Ниже приведен компонент формы для представления метода использования.
function FormComponent() {
const [text, setText] = useState(' ');
const handleSubmit = useCallback(() => {
console.log(`new test is ${text}`);
}, [text]);
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<BigTree onSubmit={handleSubmit} /> // 巨大无比的组件,不优化卡的不行
</div>
)
}
Но в настоящее время существует очень серьезная проблема, то есть наше BigTree зависит от состояния, которое слишком легко изменить. Обратный вызов В настоящее время этот обратный вызов не может быть использован в кеше.
Одним из решений для нас является определение нового экземпляра, который будет обновлять только последнее значение при повторном рендеринге, чтобы мы могли полагаться не на часто меняющееся состояние, а наuseLayoutEffect
для обновления обновленного экземпляра ссылки в .
function FormComponent() {
const [text, setText] = useState(' ');
const textRef = useRef();
useLayoutEffect(() => {
textRef.current = text;
})
const handleSubmit = useCallback(() => {
console.log(`new test is ${text}`);
}, [textRef]); // 只根据 textRef 的变化而产生变化,并不会在 text 改变就变化
return (
<div>
<input value={text} onChange={(e) => setText(e.target.value)} />
<BigTree onSubmit={handleSubmit} /> // 巨大无比的组件,不优化卡的不行
</div>
)
}
Множественные эффекты хуков обновляют сцену
useLayoutEffect
После мутации DOM запускается синхронно перед перерисовкой
это сuseEffect
Эффект тот же, оба используются для выполнения побочных эффектов, но разница в том, что эффект будет вызываться синхронно после всех изменений DOM. один сuseEffect
Большая разница в том,useLayoutEffect
синхронно иuseEffect
является асинхронным, прежде чем браузер перерисует макет страницы,useLayoutEffect
Внутренние обновления будут обновляться синхронно, но официальная рекомендация — использовать как можно больше.useEffect
чтобы не блокировать визуальные обновления.
Преимущества крючков
- Избегайте использования, когда мы повторно используем компоненты с состоянием (классы)
render props
а также高阶组件
производится, когда
Преувеличенная иерархическая вложенность.
- Чтобы мы не писали много повторяющегося кода в функции жизненного цикла для достижения этой функции.
- Указатель this в классах очень сбивает с толку.