上一篇已经基本介绍了React Hooks的基础,以及基础Hooks的使用,但还不够详细,这篇将详细的介绍基础Hooks和额外Hooks的使用。
– useReducer
1.是什么?
它是useState的替代方案,它接收一个reducer和初始值,并返回当前state及其成对的dispatch. 其中reducer是一个接收state和action并返回当前state的纯函数。(不了解action、reducer的,可以先了解一下redux)
const [state, dispatch] = useReducer(reducer, initialArg, init);
例如,我们用官网中的一个例子来说明一下:
const initialState = {count: 0};
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 Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({type: 'decrement'})}>-</button>
<button onClick={() => dispatch({type: 'increment'})}>+</button>
</>
);
}
上述代码中,在组件Counter中使用了useReducer。
- useReducer返回的dispatch方法分别用在了两个按钮上,用来执行对state的不同操作。
- 而后reducer根据参数action的类型不同,分别执行增加和减小操作,并返回操作后的state,以实现对state的更新。
2. 为什么要用useReducer?
可能很多人都习惯用useState,但是当某一个state是数组或者对象、或者一个操作能引起state多个属性发生变化,就可以使用useReducer来简化操作。
- 我们可以使用useReducer来处理请求的返回情况。
const reducer = (state, action) => {
switch(action.type){
case 'success' : alert('success');
case 'fail': alert('fail');
}
}
function Reducer(){
const [state, dispatch] = useReducer(reducer, initialState);
axios.get(
url,
).then(res => {
if (res.status === 200){
if (res.data.code === 100000) {
dispatch(type: 'success')
}else{
dispatch(type: 'fail')
}
}
})
}
3. useReducer和useState的渊源
useState是由useReducer来实现的,源码如下。当我们调用useState的时候,实际上调用的是useReducer。
function basicStateReducer(state, action) {
return typeof action === 'function' ? action(state) : action;
}
function useState(initialState) {
{
currentHookNameInDev = 'useState';
}
//返回useReducer
return useReducer(basicStateReducer, // useReducer has a special case to support lazy useState initializers
initialState);
}
– useCallback && useMemo
1. 是什么
这两个hook和useEffect的依赖参数一样,并且都会返回缓存的值,useCallback返回缓存的函数,useMemo返回缓存的变量。
const memoizedCallback = useCallback(() => {
doSomething(a, b);
},[a, b],);
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
2. 为什么要用?
我也在想,既然这两个hook和useEffect的写法是一样的,那么在什么情况下会用到呢?
首先,useEffect能有类组件中生命周期的作用。它都是自动执行的。而useCallback和useMemo会返回一个memoized 回调函数,它需要手动调用才去执行。例如一个点击事件:
import React, { useEffect, useCallback, useMemo, useState} from "react";
function CallbackCom() {
const [count, setCount] = useState(0);
handleClick = () => {
console.log('click...', count)
}
//使用useCallback的写法
const memoizedHandleClick = useCallback(() =>
console.log(`Click happened with dependency: ${count}`), [count],);
return (
<div>
<p>{count}</p>
<button onClick={handleClick}>点我</button>
</div>
)
}
export default CallbackCom;
每次组件CallbackCom重新渲染的时候,传给 button的handleClick都是一个新建的函数,所以useCallback可以在依赖项不变的时候,返回相同的引用,从而减少不必要的函数创建。同理,在使用useMemo时,可以减少不必要的一些值得计算。
– useRef
1. 是什么
useRef 返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数(initialValue)。返回的 ref 对象在组件的整个生命周期内保持不变。
const refContainer = useRef(initialValue);
2. 为什么要用useRef
在用法上,useRef和createRef几乎一样,官网上的例子如下.同样用createRef时,只需把useRef进行替换即可。
function TextInputWithFocusButton() {
const inputEl = useRef(null);// const inputEl = createRef();
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
既然写法一样,那到底有什么不一样的地方让useRef区分于createRef呢?
那就是useRef返回的 ref 对象在组件的整个生命周期内保持不变。它在每次渲染时每次都会返回同一个引用,而createRef每次都会返回不同的引用。
import React, { createRef, useRef } from "react";
const ref_list = [];
const App_useRef = () => {
const [renderIndex, setRenderIndex] = React.useState(1);
const refFromUseRef = useRef();
const refFromCreateRef = createRef();
console.log('111111');
console.log('refFromUseRef.current',refFromUseRef.current)//初始化为undefined,以后每次渲染后输出都是1
console.log('refFromCreateRef.current', refFromCreateRef.current)//初始化为null,以后每次渲染后输出都加1
//只会执行一次
if (!refFromUseRef.current) {
refFromUseRef.current = renderIndex;
}
//每次渲染后都会执行
if (!refFromCreateRef.current) {
refFromCreateRef.current = renderIndex;
}
return (
<>
<p>Current render index: {renderIndex}</p>
<p>
<b>refFromUseRef</b> value: {refFromUseRef.current}
</p>
<p>
<b>refFromCreateRef</b> value:{refFromCreateRef.current}
</p>
<button onClick={() => setRenderIndex(renderIndex+1)}>
Cause re-render
</button>
</>
);
};
export default App_useRef;
由于useRef会一直返回同一个引用,所以它返回的一直都是最开始的那个renderIndex;而createRef在每次渲染后都返回新的引用,所以它的返回值是每次新的renderIndex。
说到这里,有个疑问,为什么useRef每次的返回值一直都是初始化时的值,那么怎么样才能让它和createRef一样获取到最新的值呢?
3. useRef获取最新的值
const App_useRef = () => {
const [count,setCount] = React.useState(0);
const useref = useRef();
useEffect(()=>{
console.log('count...', count);
useref.current = count;
})
return(
<p>{count}</p>
<p>{useref.current}</p>
<button onClick={() => setCount(count+1)}>cause re- render</button>
)
}
由于useEffect会在每次re-render后执行,所以在useEffect中输出的count是每次更新后的值,此时useref.current也就能表示最新的count的值。
有一点需要说明的是,它表示的值虽然更新了,但是新值并没有能实时的显示到界面上,也就是界面上显示的useref.current永远是上一次的值。
剩余的三个hook: useImperativeHandle、useLayoutEffect、useDebugValue可以参考官方文档进行简单了解。
希望大家能持续关注哦,留一些个人信息方便大家找到我哦。知乎 github
附上个人公众号哦,希望大家前来骚扰(也会经常写一些随笔来记录生活,毕竟人生漫漫)