一、Hook和React类组件
为什么使用Hook?
- 先说下
class组件的不足
- 在
class组件之间复用状态逻辑很难(render props(渲染属性)和高阶组件,会导致层级冗余) - 复杂组件变得难以理解
- 难以理解的 class
this指向混乱
Hook优势
- 函数组件无this指向问题
- 方便复用状态逻辑
- 副作用的关注点分离
二、Hook
1、Hook简介
问题列表
- Hook 是什么?
- 什么时候我会用 Hook?
Hook 是一些可以让你在函数组件里 “钩入” React state 及生命周期等特性的函数。
Hooks解决的问题:
- 可以将组件之间的共同逻辑提取处理,共享状态逻辑
- 业务逻辑更统一,避免了同个一逻辑分散的不同的生命周期函数种,写法更统一。
2、Hook API
基础 Hook:
useStateuseEffectuseContext
额外的 Hook:
useReduceruseCallbackuseMemouseRefuseImperativeHandleuseLayoutEffectuseDebugValue
2、State Hook
useState问题列表:
- 调用 useState 方法的时候做了什么?
- useState 需要哪些参数?
- useState 方法的返回值是什么?
- useState 与 class 组件中的 setState 方法有何不同?
- 如果新的 state 需要通过使用先前的 state 计算得出,useState怎么处理?
3、Effect Hook
Effect问题列表:
- useEffect 做了什么?
- 为什么在组件内部调用 useEffect?
- useEffect 会在每次渲染后都执行吗?
effect与componentDidMount或componentDidUpdate有何不同?执行时机有和区别?- 为什么要在 effect 中返回一个函数?
- React 何时清除 effect?
- 为什么每次更新的时候都要运行 Effect?
- 怎么通过跳过 Effect 进行性能优化?
useEffect 就是一个 Effect Hook,给函数组件增加了操作副作用的能力。它跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途,只不过被合并成了一个 API。可以访问到组件的 props 和 state。
4、Hook规则
Hook 就是 JavaScript 函数,但是使用它们会有两个额外的规则:
- 只能在函数最外层调用 Hook。不要在循环、条件判断或者子函数中调用。?
- 只能在 React 的函数组件中调用 Hook。不要在其他 JavaScript 函数中调用。
在单个组件中使用多个 State Hook 或 Effect Hook,React 怎么知道哪个 state 对应哪个 useState?
5. useContext
import React, {createContext, useContext} from 'react';
const CountContext = createContext();
function App(props) {
return (
<div>
<button onClick={()=> {setCount(count+1);console.log(count)}}>click:{count}</button>
{
count % 2 ?
<span id='size'>size: {size.width} x {size.height}</span>
: <p id='size'>size: {size.width} x {size.height}</p>
}
<CountContext.Provider value={count}>
<Counter/>
</CountContext.Provider>
</div>
);
}
function Counter() {
const count = useContext(CountContext);
return (
<div>
这是count:
<h1>{count}</h1>
</div>
)
}
6. useMemo
把“创建”函数和依赖项数组作为参数传入 useMemo,它仅会在某个依赖项改变时才重新计算 memoized 值。这种优化有助于避免在每次渲染时都进行高开销的计算。
传入 useMemo 的函数会在渲染期间执行。请不要在这个函数内部执行与渲染无关的操作,诸如副作用这类的操作属于 useEffect 的适用范畴,而不是 useMemo。
import React, { useState, useMemo } from 'react'
export default function MemoCallback() {
const [count, setCount] = useState(0);
const double = useMemo(() => {
return count * 2;
}, [count === 9])
return (
<div>
<button onClick={() => {setCount(count + 1)}}>click: {count}</button>
<span>double: {double}</span>
</div>
)
}
7. useCallback
useCallback是useMemo一种分类。当useMemo返回是一个函数的时候,等级于useCallback。
useCallback(fn, deps) 相当于 useMemo(() => fn, deps)
8. useRef
用途:
- 获取子组件或者DOM节点的句柄
- 渲染周期之间共享数据的存储
// 可以利用 useRef 实现自动计时器,因为 useRef 能够记录上次数据
import React, {useState, useMemo, memo, useRef, useCallback, useEffect, PureComponent} from 'react'
class Counter extends PureComponent{
speak() {
console.log('hahhah')
}
render() {
const {props} = this;
return (
<h1 onClick={props.onClick}>{props.count}</h1>
)
}
}
export default function UseRef(props) {
const [count, setCount] = useState(0);
const [clickCount, setClickCount] = useState(0)
const counterRef = useRef();
let it = useRef();
const onClick = useCallback(() => {
setClickCount((clickCount)=> clickCount + 1)
console.log(counterRef.current)
},[counterRef])
useEffect(() => {
it.current = setInterval(() => {
setCount(count => count + 1)
}, 1000)
}, []);
useEffect(() => {
console.log(234)
if(count > 6) {
clearInterval(it.current)
}
})
return (
<div>
<span>{count}</span>
<Counter ref={counterRef} count={double} onClick={onClick}/>
</div>
)
}
9. 自定义Hook
概念:
- 自定义 Hook 是一个函数,其名称以 “use” 开头,函数内部可以调用其他的 Hook。
- 自定义 Hook 是一种自然遵循 Hook 设计的约定,而并不是 React 的特性。
使用注意:
- 自定义 Hook 必须以 “use” 开头吗?
- 在两个组件中使用相同的 Hook 会共享 state 吗?
- 自定义 Hook 如何获取独立的 state?
- 如何在多个 Hook 之间传递信息?
import React, {useState, useRef, useCallback, useEffect} from 'react'
// 自定义hook
function useCount(defaultValue) {
const [count, setCount] = useState(defaultValue);
let it = useRef();
useEffect(() => {
it.current = setInterval(() => {
setCount(count => count + 1)
}, 1000)
}, []);
useEffect(() => {
if(count > 6) {
clearInterval(it.current)
}
});
return [count, setCount]
}
// 自定义hook
function useSize() {
const [size, setSize] = useState({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
});
const onResize = useCallback(() => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
})
}, [])
useEffect(() => {
window.addEventListener('resize', onResize, false);
return () => {
window.removeEventListener('resize', onResize, false);
}
}, [])
return size;
}
// 自定义hook
function useCounter(count) {
const size = useSize();
return (
<h1>{count}
size: {size.width} X {size.height}</h1>
)
}
// 函数组件
export default function ZidingyiHook(props) {
const [count, setCount] = useCount(0);
const Counter = useCounter(count)
let size = useSize();
return (
<div>
{Counter}
<p>{size.width} x {size.height}</p>
</div>
)
}
10. Hook常见问题
- 类实例成员变量如何映射到Hook?
- Hook 中如何获取历史props 和state
- 如何强制更新一个Hoos组件?