Это первая статья из серии React Advanced. Эта серия будет включать в себя некоторые новые знания и принципы React. Те, кому интересно, могут продолжать обращать внимание.
Примечание. Хуки были официально выпущены только в React 16.8.
Зачем использовать хуки
Проблема вложения компонентов
Раньше, если нам нужно было извлечь некоторую повторяющуюся логику, мы выбирали HOC или реквизиты рендеринга. Но реализуя компоненты таким образом, когда вы открываете React DevTools, вы обнаружите, что компоненты заключены в различные другие компоненты. Этот метод сначала усложняет отладку, а также трудно добиться общего состояния.
Однако, если повторяющаяся логика извлечена с помощью хуков, вложенность компонентов не будет увеличена, и можно будет реализовать совместное использование состояния.
проблема компонента класса
Если нам нужен компонент, который управляет состоянием, то мы должны использовать метод класса для создания компонента. Но как только компоненты класса становятся сложными, разрозненный код становится не так просто поддерживать. Кроме того, код, скомпилированный компонентом класса через Babel, намного больше, чем код функционального компонента.
Хуки позволяют нам управлять состоянием через функциональные компоненты, а также могут записывать разрозненную бизнес-логику в хуки для повторного использования и обслуживания.
Как использовать хуки
Ранее я упоминал о некоторых преимуществах хуков, а теперь давайте перейдем к делу и изучим несколько часто используемых хуков, реализуя счетчик.
useState
useState
Использование очень простое, введите начальныйstate
, который возвращаетstate
и изменитьstate
Функция.
// useState 返回的 state 是个常量
// 每次组件重新渲染之后,当前 state 和之前的 state 都不相同
// 即使这个 state 是个对象
const [count, setCount] = useState(1)
setCount
использование иsetState
Точно так же можно передать новое состояние или функцию.
setCount(2)
setCount(prevCount => prevCount + 1)
useState
Использование не очень простое. Если нам нужно реализовать счетчик сейчас, то его можно написать только в виде класса по предыдущему способу, но теперь мы можем реализовать эту функцию через функциональные компоненты + хуки.
function Counter() {
const [count, setCount] = React.useState(0)
return (
<div>
Count: {count}
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</div>
);
}
useEffect
Теперь наши требования к таймеру снова были обновлены и должны быть вобновление компонентаЧтобы распечатать текущий счет позже, в это время мы можем передатьuseEffect
реализовать
function Counter() {
const [count, setCount] = React.useState(0)
React.useEffect(() => {
console.log(count)
})
return (
<div>
Count: {count}
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</div>
);
}
Приведенный выше код будет печатать правильный счет, когда мы изменим счет, мы можем в основном поставитьuseEffect
рассматривается какcomponentDidUpdate
, разницу мы можем увидеть в следующем примере.
Кроме тогоuseEffect
Вы также можете вернуть функцию, которая работает какcomponentWillUnmount
function Counter() {
const [count, setCount] = React.useState(0)
React.useEffect(() => {
console.log(count)
return () => console.log('clean', count)
})
// ...
}
Каждый раз, когда мы обновляем счетчик, он печатается первымclean
эта строка журнала
Теперь наши требования снова были обновлены, требуя, чтобы мы распечатывали счет с задержкой в две секунды после обновления счетчика. Достичь этого не может быть проще, давайте изменим егоuseEffect
код внутри
React.useEffect(() => {
setTimeout(() => {
console.log(count)
}, 2000)
})
Когда мы быстро нажимаем кнопку, мы можем увидеть правильный счет после двухсекундной задержки. Но если мы напишем этот код какcomponentDidUpdate
, все стало иначе.
componentDidUpdate() {
setTimeout(() => {
console.log(this.state.count)
}, 2000)
}
Для этого кода, если мы быстро нажмем кнопку, вы увидите те же несколько отсчетов, напечатанных после двухсекундной задержки. Это потому, что вuseEffect
Мы фиксируем правильный счет каждый раз с помощью замыкания. Но когдаcomponentDidUpdate
в, черезthis.state.count
Таким образом вы можете получить только самое последнее состояние, так как это объект.
Конечно, если вы просто хотите получить последниеstate
тогда вы можете использоватьuseRef
реализовать.
function Counter() {
const [count, setCount] = React.useState(0)
const ref = React.useRef(count)
React.useEffect(() => {
ref.current = count
setTimeout(() => {
console.log(ref.current)
}, 2000)
})
//...
}
useRef
Его можно использовать для хранения любого значения, которое будет меняться, решая проблему невозможности хранения данных через экземпляры функциональных компонентов. Также вы можетеuseRef
для доступа к данным до изменения.
function Counter() {
const [count, setCount] = React.useState(0)
const ref = React.useRef()
React.useEffect(() => {
// 可以在重新赋值之前判断先前存储的数据和当前数据的区别
ref.current = count
})
<div>
Count: {count}
PreCount: {ref.current}
<button onClick={() => setCount(prevCount => prevCount + 1)}>+</button>
<button onClick={() => setCount(prevCount => prevCount - 1)}>-</button>
</div>
//...
}
Теперь требование снова обновлено, нам нужно получить начальный счет через интерфейс, мы передаемsetTimeout
для имитации этого поведения.
function Counter() {
const [count, setCount] = React.useState();
const [loading, setLoading] = React.useState(true);
React.useEffect(() => {
setLoading(true);
setTimeout(() => {
setCount(1);
setLoading(false);
}, 2000);
});
return (
<div>
{!loading ? (
<div>
Count: {count}
<button onClick={() => setCount(pre => pre + 1)}>+</button>
<button onClick={() => setCount(pre => pre - 1)}>-</button>
</div>
) : (
<div>loading</div>
)}
</div>
);
}
Если вы приступите к выполнению этого кода, вы обнаружитеuseEffect
Неограниченное исполнение. Это потому, что вuseEffect
Внутренне обновление состояния запускается снова, поэтомуuseEffect
будет выполняться снова.
Для решения этой проблемы мы можемuseEffect
Второй параметр решения
React.useEffect(() => {
setLoading(true);
setTimeout(() => {
setCount(1);
setLoading(false);
}, 2000);
}, []);
Второй параметр передается в массиве зависимостей, который будет запущен снова, только если будут изменены зависимые свойства.useEffect
исполнение. В приведенном выше примере мы передаем пустой массив для представления этогоuseEffect
будет выполняться только один раз.
Теперь наш код немного уродлив, мы можем извлечь эту часть кода запроса в функцию, вы можете написать так
const fetch = () => {
setLoading(true);
setTimeout(() => {
setCount(1);
setLoading(false);
}, 2000);
}
React.useEffect(() => {
fetch()
}, [fetch]);
Но проблема с этим кодом та же, что и в начале, он все равно будет выполняться бесконечно. Это потому, что, хотя вы передаете зависимости, каждый раз, когда компонент обновляетсяfetch
будет воссоздан, поэтомуuseEffect
Думайте, что зависимость была обновлена, поэтому снова выполните обратный вызов.
Чтобы решить эту проблему, нам нужно использовать новый HooksuseCallback
. Этот хук может генерировать обратный вызов, который не создается снова при обновлении компонента.Далее мы повторно модифицируем код через этот хук
const fetch = React.useCallback(() => {
setLoading(true);
setTimeout(() => {
setCount(1);
setLoading(false);
}, 2000);
}, [])
React.useEffect(() => {
fetch()
}, [fetch]);
Готово, мы сделали именно то, что могли бы сделать с компонентом класса с парой компонентов Hooks + function.
Суммировать
Благодаря требованиям нескольких счетчиков мы узнали о некоторых часто используемых хуках, а затем резюмируем содержание этой части.
- useState: передать нужное нам начальное состояние и вернутьпостоянныйСостояние и функции, меняющие состояние
- useEffect: первый параметр принимает обратный вызов, который будет выполняться каждый раз при обновлении компонента, а обратный вызов может возвращать функцию, которая будет выполняться перед уничтожением каждого компонента. если
useEffect
Есть свойства, которые зависят от извне, и есть надежда, что свойства-зависимости не изменятся и не будут выполняться повторноuseEffect
Затем передайте массив как второй параметр, зависящий - useRef: если вам нужно место для хранения измененных данных
- useCallback: если вам нужен обратный вызов, который не будет воссоздан с обновлениями компонентов
Кроме того, я также инкапсулировал несколько часто используемых Hooks API, если вам интересно, вы можете прочитать это.код, код в репозитории будет постоянно обновляться.
наконец
Из этой статьи мы узнали, как использовать хуки. Если у вас есть какие-либо вопросы, пожалуйста, не стесняйтесь общаться со мной в области комментариев.
Все мои циклы статей будут в моемGithubОн обновляется первым, и желающие могут обратить на него внимание. В этом году я в основном перепишу следующие три столбца.
- Переучи JS
- Реагировать продвинуто
- Переопределить компонент
Наконец, если вы считаете, что контент полезен, вы можете обратить внимание на мой официальный аккаунт «Внешняя часть действительно забавная», вас ждет много хорошего.