представлять
react hooks
даReact 16.8
новые особенности. Это позволяет нам использовать в функциональных компонентахstate
, жизненный цикл и многое другоеreact
характеристики, не ограничиваясьclass
компоненты
react hooks
внешний видreact
Не будет компонентов без состояния, только компоненты класса и компоненты функций.
В сравнении
Существовать значит быть разумным,hooks
Он не исключение, его появление говорит о том, что ему нужно решить какие-то проблемы.class
Дефекты или недостатки компонентов, тогда давайте сначала посмотримclass
Чего не хватает или проблема существует в компоненте
По интернету сделал вывод по трем пунктам, конечно, у каждой проблемы есть свое решение
вопрос | решение | недостаток |
---|---|---|
Жизненный цикл раздут и логически связан | без | |
Логику сложно использовать повторно | Решено по наследству | Множественное наследование не поддерживается |
Решено hoc | Добавит дополнительное вложение компонентов, а также окажет некоторое влияние на производительность. | |
свойства рендеринга | То же самое, раздутая иерархия, влияние на производительность | |
class this указать на проблему |
анонимная функция | Каждый раз создавать новую функцию, повторяя ненужный рендеринг дочерних компонентов |
bind |
Нужно написать много кода, который не имеет ничего общего с логикой и состоянием |
а такжеhooks
Есть лучшие решения этих проблем
- никого не осталось
class,
Естественно нетthis
указать на проблему - путем настройки
useEffect
решить проблему повторного использования - используя
useEffect
Чтобы разделить логику и уменьшить сцену раздутой логики
Конечно,hooks
Это палка о двух концах.Если вы используете его хорошо, вы можете добиться эффекта.Если вы используете его плохо, это снизит эффективность и качество разработки.Потом мы посмотрим, как его использовать лучше.hooks
Бар
конкретное использование
Использование состояния использования
Простой пример
hooks
Способность заключается в том, чтобы позволить нам использовать в функциональных компонентахstate
Это черезuseState
Для достижения рассмотрим простой пример
function App () {
const [ count, setCount ] = useState(0)
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
useState
очень прост в использовании, мы начинаем сReact
ЗалезайuseState
После этого вам нужно только звонить прямо туда, где вы его используете.useState
функция может,useState
вернет массив, первое значение будет нашимstate,
Второе значение - это функция, используемая для измененияstate
, Тогда почему это называется здесьcount
а такжеsetCount
? Вы должны называть это, используется здесьes6
присваивание деструктурирования, так что вы можете дать ему любое имя,updateCount
, doCount
,any thing
, конечно, ради стандартов кодирования рекомендуется использовать соглашение об именах единообразно, особенно второе значение
то же значение
когда мы используемuseState
Когда одно и то же значение передается при изменении значения, будет ли наш компонент перерисовываться, например, как это
function App () {
const [ count, setCount ] = useState(0)
console.log('component render count')
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count)}}>点我</button>
</div>
)
}
Результата нет, пользуйтесь смело
значение по умолчанию useState
useState
Поддерживает нас, чтобы напрямую передать значение при вызове, чтобы указатьstate
значение по умолчанию, как этоuseState(0)
, useState({ a: 1 })
, useState([ 1, 2 ])
, также позволяет нам передать функцию для логического вычисления значения по умолчанию, например эту
function App (props) {
const [ count, setCount ] = useState(() => {
return props.count || 0
})
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
В это время маленький партнер попросил, чтобы каждый раз, когда мой компонент рендерился,useState
Будут ли функции вuseState
Функция in будет выполнена только один раз, мы можем сделать тест
function App (props) {
const [ count, setCount ] = useState(() => {
console.log('useState default value function is call')
return props.count || 0
})
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
оказаться
Получить значение предыдущего раунда, когда setUseState
мы используемuseState
Второй параметр, мы хотим получитьstate
значение, нужно толькоuseState
Возвращается второй параметр, который используется в нашем примере выше.setCount
При использовании передать параметр, параметром функции является предыдущий раундstate
значение
setCount((count => count + 1)
Случай нескольких useStates
useState
Мы не можем использовать только один, когда мы используем несколькоuseState
когда этоreact
Как определить какой из них какой на самом деле очень просто, он записывается по порядку первого исполнения, что эквивалентно хранению каждого компонентаuseState
где массив, каждый раз используется новыйuseState
, в массивpush
ОдинuseState
, то конечно когда меняем, складываем, вычитаем во время выполненияuseState
час,react
Может еще нормально работать?
function App (props) {
let count, setCount
let sum, setSum
if (count > 2) {
[ count, setCount ] = useState(0)
[ sum, setSum ] = useState(10)
} else {
[ sum, setSum ] = useState(10)
[ count, setCount ] = useState(0)
}
return (
<div>
点击次数: { count }
总计:{ sum }
<button onClick={() => { setCount(count + 1); setSum(sum - 1)}}>点我</button>
</div>
)
}
когда мы меняем во время выполненияuseState
порядок, данные будут загромождены, увеличиваяuseState
, программа сообщит об ошибке
Не вызывайте циклы, условные операторы или вложенные функции.
Hook
, убедитесь, что всегда в вашемReact
Их вызывает функция верхнего уровня. Соблюдая это правило, вы можете быть увереныHook
Звоните в одном и том же порядке в каждом рендеринге. Это позволяетReact
способный к множествуuseState
а такжеuseEffect
удержание между звонкамиhook
правильное состояние
Также рекомендуется использоватьeslint-plugin-react-hooks
Плагины для стандартизации написания кода и проверки этой ситуации
useState
Использование так просто, я узнал, Далее, давайте посмотримuseEffect
использование
Использование useEffect
Effect Hook
Позволяет выполнять операции с побочными эффектами в функциональных компонентах. Здесь упоминаются побочные эффекты. Что такое побочные эффекты? Кроме логики, связанной с состоянием, такой как сетевые запросы, прослушивание событий, поискdom
Простой пример
Есть такое требование, что нам нужно менять компонент при обновлении состоянияdocument.title
, в прошлом мы бы написали такой код
class App extends PureComponent {
state = {
count: 0
}
componentDidMount() {
document.title = count
}
componentDidUpdate() {
document.title = count
}
render () {
const { count } = this.state
return (
<div>
页面名称: { count }
<button onClick={() => { this.setState({ count: count++ })}}>点我</button>
</div>
)
}
}
использоватьhooks
как писать
function App () {
const [ count, setCount ] = useState(0)
useEffect(() => {
document.title = count
})
return (
<div>
页面名称: { count }
<button onClick={() => { setCount(count + 1 )}}>点我</button>
</div>
)
}
useEffect
Что это такое, давайте сначала проигнорируем его и вернемся к нашему резюмеclass
проблемы с компонентами,useState
Просто позвольте нашему функциональному компоненту использоватьstate
способность, то мы должны решитьclass
Для проблем компонентов решим первую, проблему раздутого жизненного цикла.
Жизненный цикл useEffect
если вы знакомы с
React class
функцию жизненного цикла, вы можете поставитьuseEffect Hook
рассматривается какcomponentDidMount
,componentDidUpdate
а такжеcomponentWillUnmount
Сочетание этих трех функций.
В прошлом мы привязывали события, отвязывали события, устанавливали таймеры, находилиdom
когда черезcomponentDidMount
,componentDidUpdate
,componentWillUnmount
жизненный цикл для достижения, иuseEffect
будет выполняться в компоненте каждый разrender
После вызова это эквивалентно этим трем функциям жизненного цикла, но вы можете решить, следует ли вызывать, передав параметры
Отмечается, что,useEffect
возвращает функцию обратного вызова, которая очищает состояние, оставшееся от последнего побочного эффекта, еслиuseEffect
Вызывается только один раз, функция обратного вызова эквивалентнаcomponentWillUnmount
Жизненный цикл
См. пример ниже
function App () {
const [ count, setCount ] = useState(0)
const [ width, setWidth ] = useState(document.body.clientWidth)
const onChange = () => {
setWidth(document.body.clientWidth)
}
useEffect(() => {
window.addEventListener('resize', onChange, false)
return () => {
window.removeEventListener('resize', onChange, false)
}
})
useEffect(() => {
document.title = count
})
return (
<div>
页面名称: { count }
页面宽度: { width }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
Следуя нашему предыдущему простому примеру, мы должны иметь дело с двумя видами логики побочных эффектов в приведенном выше примере, здесь мы должны иметь дело с обоимиtitle
, но и следить за изменением ширины экрана, согласноclass
Написано так: нам приходится иметь дело с этими двумя видами логики в жизненном цикле, но вhooks
, нам нужно только дваuseEffect
может решить эти проблемы, как мы упоминали ранее,useEffect
Может вернуть функцию для очистки состояния, оставленного последним побочным эффектом. Мы можем использовать это место, чтобы отвязать прослушиватели событий. В этом месте есть проблема, то естьuseEffect
каждый разrender
Затем он будет вызываться, например.title
изменение, эквивалентноеcomponentDidUpdate
, но наш обработчик событий не должен быть каждый разrender
После этого выполняем привязку и отвязку, что нам и нужноuseEffect
сталиcomponentDidMount
, его функция возврата становитсяcomponentWillUnmount
, здесь нужно использоватьuseEffect
Второй параметр функции
Второй параметр useEffect
useEffect
Второй параметр , возможны три случая
- Ничто не проходит, каждый компонент
render
ПозжеuseEffect
вызовет, что эквивалентноcomponentDidMount
а такжеcomponentDidUpdate
- Передача пустого массива [] будет вызываться только один раз, что эквивалентно
componentDidMount
а такжеcomponentWillUnmount
- Передавать массив, содержащий переменные, только тогда, когда эти переменные изменяются,
useEffect
будет выполнять
См. пример ниже
function App () {
const [ count, setCount ] = useState(0)
const [ width, setWidth ] = useState(document.body.clientWidth)
const onChange = () => {
setWidth(document.body.clientWidth)
}
useEffect(() => {
// 相当于 componentDidMount
console.log('add resize event')
window.addEventListener('resize', onChange, false)
return () => {
// 相当于 componentWillUnmount
window.removeEventListener('resize', onChange, false)
}
}, [])
useEffect(() => {
// 相当于 componentDidUpdate
document.title = count
})
useEffect(() => {
console.log(`count change: count is ${count}`)
}, [ count ])
return (
<div>
页面名称: { count }
页面宽度: { width }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
Согласно результатам выполнения приведенного выше примера, первыйuseEffect
середина'add resize event'
будет выведен только один раз при первом запуске, независимо от компонентаrender
, ни один не будет выводиться, второйuseEffect
будет в каждом компонентеrender
выполнить после этого,title
меняется при каждом клике, третийuseEffect
, пока есть на первом прогоне иcount
Он будет выполняться только тогда, когда экран изменится, а экран изменится.render
не влияет на третийuseEffect
useContext
оreact
как использоватьcontext
, я не буду здесь вдаваться в подробности, вы можете прочитать то, что я писал ранееРеагировать в контексте использования
context
серединаProvider
а такжеConsumer
, который можно использовать как в компонентах класса, так и в компонентах функций,contextType
Может использоваться только в компонентах класса, потому что это статическое свойство класса, как его использоватьuseContext
Хорошо, посмотрите на пример ниже
// 创建一个 context
const Context = createContext(0)
// 组件一, Consumer 写法
class Item1 extends PureComponent {
render () {
return (
<Context.Consumer>
{
(count) => (<div>{count}</div>)
}
</Context.Consumer>
)
}
}
// 组件二, contextType 写法
class Item2 extends PureComponent {
static contextType = Context
render () {
const count = this.context
return (
<div>{count}</div>
)
}
}
// 组件一, useContext 写法
function Item3 () {
const count = useContext(Context);
return (
<div>{ count }</div>
)
}
function App () {
const [ count, setCount ] = useState(0)
return (
<div>
点击次数: { count }
<button onClick={() => { setCount(count + 1)}}>点我</button>
<Context.Provider value={count}>
<Item1></Item1>
<Item2></Item2>
<Item3></Item3>
</Context.Provider>
</div>
)
}
Запустив приведенный выше пример, мы получаем результат, что все три метода записи могут удовлетворить наши потребности, но каждый из трех методов записи имеет свои преимущества и недостатки.Ниже приведен результат сравнения.
написание | Преимущества и недостатки |
---|---|
consumer |
сложная вложенность,Consumer Первый дочерний узел должен быть функцией, которая незаметно увеличивает нагрузку. |
contextType |
Поддерживает только компоненты класса, не может использоваться в несколькихcontext использовать в случае |
useContext |
Нет необходимости в вложенности, большеcontext Просто написать |
С вышеупомянутым сравнением, нет причин продолжать использоватьconsumer
а такжеcontextType
useMemo
useMemo
что это, сmemo
это имеет значение,memo
Конкретный контент можно просмотретьИспользование оптимизации производительности, памятки, PureComponent, shouldComponentUpdate в React,если быть честнымmemo
функциональный компонентPureComponent
, используется для оптимизации производительности,useMemo
Слишком,useMemo
по моему впечатлению иVue
изcomputed
Вычисляемые свойства похожи тем, что они вычисляют результат на основе значения зависимости.Когда значение зависимости не изменяется, изменение состояния не запускается.useMemo
Как его использовать, смотрите в примере ниже
function App () {
const [ count, setCount ] = useState(0)
const add = useMemo(() => {
return count + 1
}, [count])
return (
<div>
点击次数: { count }
<br/>
次数加一: { add }
<button onClick={() => { setCount(count + 1)}}>点我</button>
</div>
)
}
В приведенном выше примереuseMemo
Также поддерживает передачу второго параметра, использование иuseEffect
аналогичный
- Не передавайте массив, он будет пересчитываться при каждом обновлении
- Пустой массив, оценивается только один раз
- Зависит от соответствующего значения, при изменении соответствующего значения оно будет пересчитано (можно полагаться на другое
useMemo
возвращаемое значение)
должны знать о том,useMemo
будет выполняться при рендеринге, а не после рендеринга, что аналогичноuseEffect
Различия есть, так чтоuseMemo
Не рекомендуется иметь логику, связанную с побочными эффектами
в то же время,useMemo
Его можно использовать как средство оптимизации производительности, но не воспринимайте его как семантическую гарантию.React
может «забыть» некоторые из предыдущихmemoized
значения и пересчитать их при следующем рендеринге
useCallback
useCallback
что это, так сказатьuseMemo
синтаксический сахар дляuseCallback
реализовано, можно использоватьuseMemo
, В React мы часто сталкиваемся с проблемой оптимизации рендеринга подкомпонентов, подробности можно посмотретьИспользование оптимизации производительности, памятки, PureComponent, shouldComponentUpdate в React, особенно при передаче свойств функции дочерним компонентам, каждый разrender
Будут созданы новые функции, что приведет к ненужному рендерингу подкомпонентов и потере производительности.useCallback
место использования,useCallback
гарантировано, независимо отrender
Сколько раз наши функции являются одной и той же функцией, уменьшая накладные расходы на непрерывное создание, см. следующий пример, как его использовать.
const onClick = `useMemo`(() => {
return () => {
console.log('button click')
}
}, [])
const onClick = useCallback(() => {
console.log('button click')
}, [])
такой же,useCallback
Второй параметр иuseMemo
то же самое, без разницы
useRef
useRef
Что он делает? На самом деле это очень просто. Есть два способа его использования.
- Получить экземпляр дочернего компонента (доступны только компоненты класса)
- Глобальная переменная в функциональном компоненте, не из-за дублирования
render
Повторное объявление, аналогичное объявлению компонентов классаthis.xxx
Получить экземпляр дочернего компонента
Как указано выше,useRef
Могут быть получены только экземпляры подкомпонентов, то же самое для компонентов класса, см. следующий пример для деталей.
// 使用 ref 子组件必须是类组件
class Children extends PureComponent {
render () {
const { count } = this.props
return (
<div>{ count }</div>
)
}
}
function App () {
const [ count, setCount ] = useState(0)
const childrenRef = useRef(null)
// const
const onClick = useMemo(() => {
return () => {
console.log('button click')
console.log(childrenRef.current)
setCount((count) => count + 1)
}
}, [])
return (
<div>
点击次数: { count }
<Children ref={childrenRef} count={count}></Children>
<button onClick={onClick}>点我</button>
</div>
)
}
useRef
При использовании вы можете передать значение по умолчанию, чтобы указать значение по умолчанию. Когда вам нужно его использовать, посетитеref.current
Вы можете получить доступ к экземпляру компонента
свойства компонента класса
В некоторых случаях нам нужно убедиться, что функциональный компонент каждый разrender
После этого некоторые переменные не будут объявляться повторно, напримерDom
узел, таймерid
Подождите, в компоненте класса мы можем полностью сохраниться, добавив в класс кастомное свойство, скажемthis.xxx
Но функциональный компонент неthis
Естественно, я не могу использовать этот метод, некоторые друзья говорят, что я могу использоватьuseState
чтобы сохранить значение переменной, ноuseState
вызовет компонентrender
, здесь совершенно не нужно, нам просто нужно использоватьuseRef
Для достижения см. следующий пример
function App () {
const [ count, setCount ] = useState(0)
const timer = useRef(null)
let timer2
useEffect(() => {
let id = setInterval(() => {
setCount(count => count + 1)
}, 500)
timer.current = id
timer2 = id
return () => {
clearInterval(timer.current)
}
}, [])
const onClickRef = useCallback(() => {
clearInterval(timer.current)
}, [])
const onClick = useCallback(() => {
clearInterval(timer2)
}, [])
return (
<div>
点击次数: { count }
<button onClick={onClick}>普通</button>
<button onClick={onClickRef}>useRef</button>
</div>
)
}
Когда мы используем обычную кнопку, чтобы приостановить таймер, обнаруживается, что таймер не может быть очищен, потому чтоApp
компонент каждый разrender
, повторно заявитьtimer2
, таймерid
во-вторыхrender
потерян, поэтому таймер не может быть очищен.В этом случае вам нужно использоватьuseRef
, чтобы держать таймер для насid
, похожий наthis.xxx
,ЭтоuseRef
другое использование
useReducer
useReducer
Что это, это на самом деле что-то вродеredux
функция в , по сравнению сuseState
, он больше подходит для какой-то более сложной логики и содержит несколько подзначений, или следующийstate
зависит от предыдущегоstate
и т. д. конкретные сценарии,useReducer
В общей сложности три параметра
- Первый параметр — это
reducer
, это функция типа(state, action) => newState
функция, переходящая в предыдущуюstate
и на этот разaction
- Второй параметр является начальным.
state
, который является значением по умолчанию, является более простым методом - Третий параметр — ленивая инициализация, которую можно использовать для вычисления
state
Логика извлекается вreducer
внешний, который также сбрасывается для будущих парstate
изaction
облегчает обработку
См. пример ниже для конкретного использования
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function App() {
const [state, dispatch] = useReducer(reducer, {
count: 0
});
return (
<>
点击次数: {state.count}
<button onClick={() => dispatch({type: 'increment'})}>+</button>
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
</>
);
}
useImperativeHandle
useImperativeHandle
позволяет вам использоватьref
Проще говоря, при настройке значения экземпляра, предоставляемого родительскому компоненту, дочерний компонент может выборочно предоставлять некоторые методы подкомпоненту, чтобы можно было скрыть некоторые частные методы и свойства.useImperativeHandle
должно быть сforwardRef
Используйте его вместе, см. следующий пример того, как его использовать
function Kun (props, ref) {
const kun = useRef()
const introduce = useCallback (() => {
console.log('i can sing, jump, rap, play basketball')
}, [])
useImperativeHandle(ref, () => ({
introduce: () => {
introduce()
}
}));
return (
<div ref={kun}> { props.count }</div>
)
}
const KunKun = forwardRef(Kun)
function App () {
const [ count, setCount ] = useState(0)
const kunRef = useRef(null)
const onClick = useCallback (() => {
setCount(count => count + 1)
kunRef.current.introduce()
}, [])
return (
<div>
点击次数: { count }
<KunKun ref={kunRef} count={count}></KunKun>
<button onClick={onClick}>点我</button>
</div>
)
}
другие крючки
еще дваhook
, говорить не о чем, толку мало, можно глянуть официальную документацию
пользовательский крючок
Мы обобщили три вопроса ранее,class this
Указывая на проблему, проблема избыточности логики жизненного цикла была решена, и логика трудно повторно использовать.Это не было решено в предыдущем примере.Чтобы решить эту проблему, необходимо настроитьhook
решать
настроить
Hook
, вы можете извлечь логику компонентов в повторно используемые функции, чтобы решить проблему сложного повторного использования логики
Предыдущий пример — это пример получения изменения ширины экрана.Предположим, у нас есть много компонентов, которые требуют этой логики, тогда нам нужно только извлечь ее в пользовательскийhook
Вот и все, см. следующий пример для конкретной реализации
function useWidth (defaultWidth) {
const [width, setWidth] = useState(document.body.clientWidth)
const onChange = useCallback (() => {
setWidth(document.body.clientWidth)
}, [])
useEffect(() => {
window.addEventListener('resize', onChange, false)
return () => {
window.removeEventListener('resize', onChange, false)
}
}, [onChange])
return width
}
function App () {
const width = useWidth(document.body.clientWidth)
return (
<div>
页面宽度: { width }
</div>
)
}
Из приведенного выше примера мы можем видеть, что
настроитьhook
это функция, имя которой начинается сuse
В начале внутри функции могут быть вызваны другие функции.hook
, почемуuse
начинается, потому что, если он не начинается сuse
начало,React
Невозможно определить, содержит ли функция свои внутренниеhook
Звонки,React
также не сможет автоматически проверить вашhook
нарушил ли онhook
правила, поэтомуuse
начало
настроитьhook
, это действительно просто, но какую логику нужно извлекать в кастомныеhook
, что требует накопленного опыта в работе, чтобы судить, чтобы избежатьhook
а такжеhook
Суммировать
в моем обучении и использовании пользовательскихhook
В то время я обнаружил, что причина этого очень проста, многие интерфейсные фреймворки и библиотеки имеют схожие концепции, а дизайн фреймворков и библиотек в конечном итоге одинаков, поэтому, когда мы изучаем новый фреймворк, библиотеку или концепцию, мы должны не Это совершенно новая вещь, и больше должно быть выведено из содержания, которое мы освоили, и по аналогии, чтобы мы могли получить вдвое больший результат с половиной усилий при обучении, и во все более обновляемой области фронтенда, мы можем потратить больше времени, чтобы понять больше основного содержания
Наконец, если эта статья вам чем-то помогла, спасибо за лайк 💗
Ссылаться на
- react-1251415695.cos-website.AP-Chengdu.No Money cloud.com/docs/hooks-…
- реагировать JS.org/docs/hooks-…