再探React Hooks

456 阅读3分钟

这是我参与 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可能在日常工作中用的并不多,我们可能还是以useStateuseEffect为主。比如说像上文提到的useMemo、useCallback等Hooks,了解后对我们排查问题、性能调优还是有帮助的。在学习这些API的过程中,我们应该知其然,知其所以然。

最后打波小广告,美团校招社招内推,不限部门,不限岗位,不限投递数量,海量hc,快来快来~