一 什么是Hooks
Hook 是react16.8新增特性
Hook本身就是一类特殊的函数,他们可以为你的函数型组件注入一些特殊的功能,在不用编写类的情况下实用state 和其他react 特性
二 为什么要使用react Hooks
1、状态逻辑难以复用
2、组成复杂难以维护
3、类的this 指向性问题
hooks 允许我们使用简单的特殊函数实现class 各种功能
三、举例Hooks
useState
在 React 组件中,我们经常要使用 state 来进行数据的实时响应,根据 state 的变化重新渲染组件更新视图。
因为纯函数不能有状态,在 hooks 中,useState
就是一个用于为函数组件引入状态(state)的状态钩子。
const [state, setState] = useState(initialState);
useState
的唯一参数是状态初始值(initial state),它返回了一个数组,这个数组的第[0]项是当前当前的状态值,第[1]项是可以改变状态值的方法函数
延迟初始化
initialState 参数是初始渲染期间使用的状态。 在随后的渲染中,它会被忽略了。 如果初始状态是高开销的计算结果,则可以改为提供函数,该函数仅在初始渲染时执行:
function Counter({initialCount = 0}) { // 初始值为1 const [count, setCount] = useState(() => initialCount + 1); return ( <> Count: {count} <button onClick={() => setCount(0)}>Reset</button> <button onClick={() => setCount(count + 1)}>+</button> <button onClick={() => setCount(prevCount => prevCount - 1)}>-</button> </> ); }
函数式更新对比普通更新
如果需要使用前一时刻的 state(状态) 计算新 state(状态) ,则可以将 函数 传递给 setState 。该函数将接收先前 state 的值,并返回更新的 state
useReducer
useReducer 可以接受三个参数,第一个参数接收一个形如(state, action) => newState
的 reducer纯函数,使用时可以通过dispatch(action)
来修改相关逻辑。
第二个参数是 state 的初始值,它返回当前 state 以及发送 action 的 dispatch 函数。
你可以选择惰性地创建初始 state,为此,需要将 init 函数作为 useReducer 的第三个参数传入,这样初始 state 将被设置为 init(initialArg)。
//官方示例
function countReducer(state, action) {
switch (action.type) {
case 'add':
return state + 1;
case 'minus':
return state - 1;
default:
return state;
}
}
function initFunc(initialCount) { return initialCount + 1; }
function Counter({initialCount = 0}) { const [count, dispatch] = useReducer(countReducer, initialCount, initFunc); return ( <div> <p>Count: {count}</p> <button onClick={() => { dispatch({ type: 'add' }); }} > 点击+1 </button> <button onClick={() => { dispatch({ type: 'minus' }); }} > 点击-1 </button> </div> ); }
对比useState可知,看起来我们的代码好像变得复杂了,但实际应用到复杂的项目环境中,将状态管理和代码逻辑放到一起管理,使我们的代码具有更好的可读性、可维护性和可预测性。
useEffect 用于引用副作用
useEffect(create, deps);
副作用:
对环境的改变即为副作用,如修改 document.title 但我们不一定非要把副作用放在 useEffect 里面 实际上叫做 afterRender 更好,每次render后执行
用途
一、作为 componentDidMount 使用,[ ] 作第二个参数 二、作为 componentDidUpdate 使用,可指定依赖 三、作为 componentWillUnmount 使用,通过 return 四、以上三种用途可同时存在
特点:
如果同时存在多个 useEffect, 会按照出现次序执行
useLayoutEffect
useEffect 在浏览器渲染完成后执行 useLayoutEffect 在浏览器渲染前执行
function App1() { const [n, setN] = useState(0) const time = useRef(null) const onClick = () => { setN(i => i + 1) time.current = performance.now() } useLayoutEffect(() => { if (time.current) { console.log(performance.now() - time.current) //大概是0.7ms } }) useEffect(() => { if (time.current) { console.log(performance.now() - time.current) //大概是2.7ms } }) return ( <div className="App"> <h1>n: {n}</h1> <button onClick={onClick}>Click</button> </div> ); }
useMemo==》useCallback
useCallback(x => console.log(x), [m]) 等价于
useMemo( () => x => console.log(x), [m])
对比useMemo,useMemo缓存的是一个值,useCallback缓存的是一个函数,是对一个单独的props值进行缓存
举例:
- 子组件onChange调用了父组件的handleOnChange
- 父组件handleOnChange内部会执行setText(e.target.value)引起父组件更新
- 父组件更新会得到新的handleOnChange,传递给子组件,对于子组件来说接收到一个新的props
- 子组件进行不必要更新
![](https://img2020.cnblogs.com/blog/1749345/202012/1749345-20201230170427795-74719793.png)
- handleOnChange被缓存了下来,尽管父组件更新了,但是拿到的handleOnChange还是同一个
- 对比useMemo,useMemo缓存的是一个值,useCallback缓存的是一个函数,是对一个单独的props值进行缓存
- memo缓存的是组件本身,是站在全局的角度进行优化
参考 :https://juejin.im/post/6877012518183632910#heading-3
https://www.jianshu.com/p/1252be39c702