(Читать руководство:Рекомендуется вручную запускать каждую небольшую демонстрацию, чтобы убедиться, что вы будете полезны.)
Hook — это новая функция React 16.8 (включая react-dom 16.8), которая позволяет использовать состояние и другие функции React без написания класса.Hook — это специальная функция.
React-router имеет метод Hook и поддерживает ловушку, начиная с версии 5.1.
React Redux начиная с версии 7.1.0Поддержка хуков APIи подвергаетсяuseDispatch
а такжеuseSelector
Дождитесь крючка.
Далее я подробно расскажу о каждом API и дам демо. Если вы считаете, что это хорошо, нажмите 👍, чтобы поощрить его.
Лично я настоятельно рекомендую, если вы видели это тысячу раз, лучше пройти его руками.
1. Каждый Hook API не работает
1, состояние использования
useState используется для инициализации параметра состояния
import React from 'react';
function Example(){
// 声明一个count的state变量
const [count, setCount] = useState(0);
return (
<div>
<p>点击{count}次</p>
<button onClick={() => setCount(count + 1)}>点击</button>
</div>
)
}
функция использования состояния:
-
состояние чтения: это this.state.count в классе, и он подсчитывается непосредственно здесь;
-
состояние обновления: В классе обновите значение счетчика с помощью this.setState(), здесь используйте setCount;
-
Роль квадратных скобок: Используя присваивание структуры, одновременно создаются две переменные count и setCount, что эквивалентно следующему:
var demoData = useState(0); var count = demoData[0]; // 第一个值为当前的state var setCount = demoDate[1]; // 第二个值为更新state的函数
2. использоватьЭффект
useEffect выполняет побочные эффекты (хуки-функции) в функциональных компонентах
грамматика:useEffect(() => {}, [])
Получить два параметра, первый — функция, второй — массив;
import React, { useState, useEffect } from 'React';
function Example(){
const [count, setCount] = useState(0);
const btnClick = () => {
setCount(count + 1);
}
useEffect(() => {
console.log('执行了useEffect');
document.title = `点击了{count}次`
})
return (
<div>
<p>点击{count}次</p>
<button onClick={() => { btnClick()}}>点击</button>
</div>
)
}
намекать: Зная функции жизненного цикла React Class, вы можете думать об useEffect Hook как обcomponentDidMount, componentDidUpdate и componentWillUnmountСочетание этих трех функций.
useEffect выполняется каждый раз при отображении страницы. По умолчанию он выполняется после первого рендеринга и после каждого обновления; несколько useEffects могут появляться в одном и том же функциональном компоненте одновременно, и React будет вызывать каждый эффект в компоненте в том порядке, в котором объявлены useEffects.
Не нужно очищать эффекты
Способ написания класса, то есть нет необходимости выполнять операции в componentWillUnmount
componentDidMount(){
document.title = `点击了{count}次`;
}
componentDidUpdate(){
document.title = `点击了{count}次`;
}
Как написать крючок
useEffect(() => {
document.title = `点击了{count}次`;
})
Эффекты, которые необходимо очистить
То есть его нужно очистить в componentWillUnmount.Например, мы используем setInterval для обновления текущего времени. код показывает, как показано ниже
class FriendStatus extends React.Component{
constructor(props){
super(props);
this.state = { nowTime: null};
this.timer = null;
}
componentDidMount(){
this.timer = setInterval(() => {
this.setState({
nowTime: new Date()
})
}, 1000)
}
componentWillUnmount(){
if (this.timer !== null) {
clearInterval(this.timer);
}
}
render(){
let time = this.state.nowTime;
return(
<div>{time.toString()}</div>
)
}
}
Используйте крючок следующим образом:
import React, { useState, useEffect } from 'react';
function FriendStatus(props) {
const [nowTime, setNowTime] = useState(new Date());
useEffect(() => {
let timer = setInterval(() => {
setNowTime(new Date())
}, 1000)
return () => { // 返回一个清理函数
clearInterval(timer);
};
}, []);
return(<div>{nowTime.toString()}</div>)
}
Оптимизация производительности за счет пропуска эффектов
Мы знаем, что выполнение очистки или выполнение эффектов после каждого рендеринга вызовет проблемы с производительностью.В классе мы добавляем логику сравнения prevProps и prevState в componentDidUpdate для решения этой проблемы;
componentDidUpdate(prevProps, prevState){
if(this.state.count !== prevState.count){
document.title = `点击了{this.state.count}次`
}
}
Синтаксис замены эффектов эффектами в хуках следующий:
useEffect(() => {
console.log('执行了--useEffect')
document.title = `点击了{count}次`;
}, [count]); // 在初次渲染和count发生变化时更新
Если вторым параметром является пустой массив [], Effect будет выполнен один раз при начальном рендеринге и еще раз, включая функцию очистки Effect (вы можете заменить [count] в приведенном выше коде на тест []); даны два типа ниже Код выполнения для случая:
// 带清除函数即为useEffect的第一个参数(函数)中再返回一个函数
// 不带清除函数+第二个参数为[];
// --> 整个生命周期只执行一次,相当于componentDidMount;
useEffect(() => {
console.log('执行了--useEffect');
document.title = `点击了${count}次`;
}, []);
// 带清除函数+第二个参数为[];
// --> 整个生命周期中执行了两次,相当于componentDidMount和componentWillUnmount;
useEffect(() => {
console.log('执行了--useEffect');
document.title = `点击了${count}次`;
return () => { // 相当于componentWillUnmount;
console.log('执行了--清除函数');
document.title = "";
}
}, [])
Если второй параметр не добавлен, Эффект будет выполняться один раз при первом рендеринге и каждом обновлении.
3, использовать контекст
Получите объект контекста (определенный здесь как Mycontext), созданный React.createContext(), и верните значение, привязанное к значению свойства контекста; получите ближайшие реквизиты
// Mycontext为React.createContext的返回值
const Mycontext = React.createContext();
<Mycontext.provider value={}>
...
</Mycontext.provider>
--------------------------
const value = useContext(Mycontext);
Компоненты, вызывающие useContext, всегда будут повторно отображаться при изменении значения контекста.
Полная демонстрация выглядит следующим образом:
import React, { useContext } from 'react';
const themes = {
light: {
color: '#ddd',
background: 'yellow'
},
dark: {
color: '#fff',
background: '#333'
}
}
const ThemeContext = React.createContext();
function TestContext(){
return (
<ThemeContext.Provider value={themes.dark}>
<Father></Father>
</ThemeContext.Provider>
)
}
function Child(){
const theme = useContext(ThemeContext);
return (
<div style={{color: theme.color, backgroundColor: theme.background}}>测试一下useContext</div>
)
}
function Father(){
return (
<div>
<Child/>
</div>
)
}
export default TestContext;
4. использовать Редуктор
грамматика:
const [state, dispatch] = useReducer(reducer, initalArg, init);
является альтернативой useState. В некоторых сценариях useReducer подходит больше, чем useState. (Знакомы с Redux, вы знаете, как это работает)
Полная демонстрация:
import React, { useReducer } from 'react';
const init = {
count: 0
};
function reducer(state, action){
switch(action.type){
case 'add':
return {count: state.count + 1};
case 'minus':
return {count: state.count - 1};
default: throw new Error();
}
}
function TestReducer(){
const [state, dispatch] = useReducer(reducer, init);
return (
<div>
count: {state.count}
<ul>
<li><button onClick={() => dispatch({type: 'add'})}>+</button></li>
<li><button onClick={() => dispatch({type: 'minus'})}>-</button></li>
</ul>
</div>
)
}
export default TestReducer;
5. использоватьСсылка
грамматика:
const refObj = useRef(initVal);
useRef
Возвращает изменяемый объект ref, чей.current
Свойства инициализируются переданными параметрами (initValue
). Возвращенный объект ref остается неизменным на протяжении всего времени существования компонента;
Полная демонстрация:
import React, { useRef } from 'react';
export default function TestUseRef(){
// const InputEl = React.createRef(null);
const InputEl = useRef(null);
const getInputFocus = () => {
InputEl.current.placeholder = "输入中";
InputEl.current.focus();
}
return (
<div>
<input ref={InputEl} type="text" placeholder="请输入"/>
<button onClick={() => getInputFocus()}>测试useRef</button>
</div>
)
}
Тест нашел ту же функцию, что и React.createRef().
6. использоватьОбратный звонок
Синтаксис: вернуть функцию обратного вызова;
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b])
useCallback (fn, deps) эквивалентно useMemo (() => fn, deps), функция обратного вызова будет обновляться только при изменении зависимостей.
Полная демонстрация:
import React, { useState, useCallback, useEffect } from 'react';
function TestUseCallback(){
const [ count, setCount ] = useState(0);
const [ inputVal, setInputVal ] = useState('');
const backCount = useCallback(() => {
return count;
}, [count]);
return (
<div>
<button onClick={() => {setCount(count + 1)}}>Click me--点击{count}次</button>
<input value={inputVal} onChange={(e) => setInputVal(e.target.value)} />
<hr/>
<Child count={count} callback={backCount}/>
</div>
)
}
function Child(props){
const {count, callback} = props;
const [mycount, setMycount] = useState(() => callback());
useEffect(() => {
setMycount(callback());
}, [callback]);
return (
<div>
<h3>child组件</h3>
<p>myCount---{mycount}</p>
</div>
)
}
export default TestUseCallback;
7. использовать памятку
useMemo получает пользовательскую функцию и параметры массива и возвращает значение и запускает рендеринг только при изменении параметра массива.Эта оптимизация помогает избежать дорогостоящих вычислений при каждом рендеринге;
Синтаксис: вернуть значение;
const memoizedValue = useMemo(() => computedExpensiveValue(a, b),[a,b])
Полная демонстрация без useMemo:
import React, { useState, useMemo } from 'react';
function TestUseMemo(){
const [count, setCount] = useState(0);
const [value, setValue] = useState('');
const AddSum = () => {
console.log('addSum');
let sum = 0;
for(let i = 0; i < count*1000; i++){
sum += i;
}
return sum;
}
return (
<div>
<p>点击了{count}次</p>
<p>计算求和所得{AddSum()}</p>
<button onClick={() => {setCount(count + 1)}}>Click me</button>
<input value={value} onChange={event => setValue(event.target.value)}/>
</div>
)
}
export default TestUseMemo;
Обнаружено, что независимо от того, нужно ли изменить количество или изменить значение, функция AddSum() будет запущена для вычисления;
Полная демонстрация с использованием useMemo:
import React, { useState, useMemo } from 'react';
function TestUseMemo(){
const [count, setCount] = useState(0);
const [value, setValue] = useState('');
const AddSum = useMemo(() => {
console.log('useMemo');
let sum = count;
for(let i = 0; i < count*1000; i++){
sum += i;
}
return sum;
}, [count]); // 只在count值发生变化时才重新计算值
return (
<div>
<p>点击了{count}次</p>
<p>计算求和所得{AddSum}</p>
<button onClick={() => {setCount(count + 1)}}>Click me</button>
<input value={value} onChange={event => setValue(event.target.value)}/>
</div>
)
}
export default TestUseMemo;
Используя useMemo, мы обнаружили, что вычисление AddSum и повторный рендеринг выполняются повторно только при изменении значения счетчика, что позволяет избежать ненужных накладных расходов на производительность.
8. использовать эффект макета
Синтаксис такой же, как у useEffect, но время выполнения useEffect отличается;
Полная демонстрационная проверка:
import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
export default function TestuseLayout(){
const [count, setCount] = useState(0);
const [text, setText] = useState('');
const InputRef = useRef(null);
useEffect(() => {
console.log(InputRef, 'start useEffect');
document.title = `${count} times`;
return () => {
console.log(InputRef, 'end useEffect');
document.title = 'remove';
}
});
useLayoutEffect(() => {
console.log(InputRef, 'start useLayoutEffect');
document.title = `${count} times`;
return () => {
console.log(InputRef, 'end useLayoutEffect');
document.title = 'Layout remove';
}
})
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
<p>
<input ref={InputRef} value={text} onChange={(e) => setText(e.target.value)}/>
</p>
</div>
)
}
Смотрите результат выполнения следующим образом:
В заключение:
- Выполнение useLayoutEffect предшествует useEffect;
- Функция возврата первого параметра функции выполняется перед исходной функцией;
9. использовать императивхендл
useImperativeHandle позволяет выставлять пользовательские значения экземпляра родительскому компоненту при использовании ref. По возможности избегайте использования ref. useImperativeHandle следует использовать вместе с forwardRef.
грамматика:
useImperativeHandle(ref, createHandle, [deps]);
Полная демонстрация:
import React, { useImperativeHandle, useRef, forwardRef} from 'react';
function TestuseImper(){
const childRef = useRef(null);
const btnFn = () => {
console.log('childRef=', childRef);
childRef.current.focus();
}
return (
<div>
<button onClick={() => {btnFn()}}>获取子组件input的焦点</button>
<hr/>
<Child ref = {childRef}/>
</div>
)
}
function Child(props, ref){
const InputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => InputRef.current.focus();
}))
return (
<div>
<input ref={InputRef}/>
</div>
)
}
Child = forwardRef(Child);
export default TestuseImper;
После выполнения приведенной выше демонстрации мы обнаруживаем, что родительский компонент может получить ссылку дочернего компонента через открытую ссылку и изменить ее.
10. использовать отладочное значение
грамматика:
useDebugValue(value)
Полная демонстрация:
import React, { useState, useDebugValue } from 'react';
export default function TestuseLayout(){
function useMyCount(num) {
const [ count, setCount ] = useState(0);
useDebugValue(count > num ? '溢出' : '不足');
const myCount = () => {
setCount(count + 2);
}
return [ count, myCount ];
}
const [count , myCount] = useMyCount(10);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => myCount(count + 1)}>Click me</button>
</div>
)
}
---------------------------------------------------------
2. Пользовательский крючок
Пользовательские хуки могут повторно использовать некоторую логику состояния между компонентами. В режиме класса для их реализации обычно используются компоненты более высокого порядка. Пользовательские хуки позволяют нам достичь той же цели без добавления компонентов. Логику компонента можно извлечь в повторно используемую функцию ; Полная демонстрация простого пользовательского хука приведена ниже:
import React, { useState } from 'react';
function useChangeCount(init){
// 自定义hook,名字必须以use开头
const [count, setCount] = useState(init);
const plus = () => {
setCount(count + 1);
}
const minus = () => {
setCount(count - 1);
};
const reset = () => {
setCount(init);
};
return [count, {plus, minus, reset}];
}
function SelfHook(){
const [count, setCount] = useChangeCount(0);
return (
<div>
<p>当前count: {count}</p>
<div><button onClick={setCount.plus}>增加</button></div>
<div><button onClick={setCount.minus}>减少</button></div>
<div><button onClick={setCount.reset}>重置</button></div>
</div>
)
}
export default SelfHook;
Больше пользовательских хуков для чтенияИсходный код React-использования
3. Правила использования хука
Используйте хуки только на верхнем уровне
Не вызывайте хуки в циклах, условных выражениях или вложенных функциях;
Вызывать Hook только в функции компонента
где крючок называется
- Вызов хуков в функциональных компонентах
- Вызов хука в пользовательском хуке
обновление версии
Примечание. Чтобы включить хуки, все пакеты, связанные с React, должны быть обновлены до версии 16.8.0 или выше. В противном случае хук не сработает.