这是我参与 8 月更文挑战的第 5 天,活动详情查看: 8月更文挑战
接上文一文读懂React Hooks,本文会继续探索剩余一些API。
useReducer
我们先来看用法:
const [state, dispatch] = useReducer(reducer, initialArg, init);
这是一个useState 的高级版本,允许使用者可以用函数定义state的值,在一些复杂的业务场景下,用它会比较合适。它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
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>
</>
);
}
是不是有点redux的味道了?可以通过对action类型的定义,实现不同的业务逻辑。
useCallback
前面我们看到的都是一些帮助存储状态的hook方法,而useCallback是用于存储函数的,我们来看下用法:
const memoizedCallback = useCallback(
() => {
doSomething(a, b);
},
[a, b],
);
会返回一个 memoized 回调函数,比如函数防抖(debounce)、函数节流(throttle),都是需要一个上下午来存储当前的信息的,那么useCallback就派上用场了。
useMemo
useMemo(() => fn, deps) 相当于 useCallback(fn, deps)。
useMemo会返回一个状态值,这点是和useCallback是有区别的,你可以把 useMemo 作为性能优化的手段,但不要把它当成语义上的保证。因为函数组件每次都会被调用,而其中包含的一些计算是可以被优化的。在依赖项没有改变的时间,我们可以直接取上次的计算值,而不需要进行重复的计算操作。
useRef
在class组件中,你应该熟悉 ref 这一种访问 DOM 的主要方式。如果你将 ref 对象以 <div ref={myRef} />
形式传入组件,则无论该节点如何改变,React 都会将 ref 对象的 .current 属性设置为相应的 DOM 节点。
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` 指向已挂载到 DOM 上的文本输入元素
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
useImperativeHandle
useImperativeHandle相当于一个被约束的useRef。我们都知道,在一些强类型的语言中,class内部都有private、public等关键字,用于声明哪些是私有的属性或方法,哪些是公开可以访问的。这样做的目的很明显,就是防止代码失控。
useImperativeHandle 可以让你在使用 ref 时自定义暴露给父组件的实例值。
function FancyInput(props, ref) {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} />;
}
FancyInput = forwardRef(FancyInput);
包裹后,父组件只可以调用 inputRef.current.focus()。
useLayoutEffect
用法与 useEffect 相同,但它会在所有的 DOM 变更之后同步调用。
useDebugValue
主要用于调试,用于向React 开发者工具中打自定义 hook 的标签。
function useFriendStatus(friendID) {
const [isOnline, setIsOnline] = useState(null);
// ...
// 在开发者工具中的这个 Hook 旁边显示标签
// e.g. "FriendStatus: Online"
useDebugValue(isOnline ? 'Online' : 'Offline');
return isOnline;
}
小结
今天介绍的API可能在日常工作中用的并不多,我们可能还是以useState
、useEffect
为主。比如说像上文提到的useMemo、useCallback等Hooks,了解后对我们排查问题、性能调优还是有帮助的。在学习这些API的过程中,我们应该知其然,知其所以然。
最后打波小广告,美团校招社招内推,不限部门,不限岗位,不限投递数量,海量hc,快来快来~