在React开发中,我们经常使用一些通用的Hook来简化状态管理和副作用的处理。ahooks是一个非常流行的React Hook库,它提供了一系列有用的自定义Hook。但是,如果我们想要更深入地理解这些Hook背后的原理,手写它们是一个非常好的练习。在本文中,我们将手写实现ahooks中的几个常用的Hook。

useGetState

useGetState是一个类似于React自带的useState的Hook,但它返回一个函数,允许我们在任何时候获取最新的状态值,而不仅仅是在组件渲染时。下面是如何实现它的代码:

import { useState, useCallback, useRef, useEffect } from 'react';

function useGetState(initialValue) {
  const [state, setState] = useState(initialValue);
  const stateRef = useRef(state);

  useEffect(() => {
    stateRef.current = state;
  }, [state]);

  const getState = useCallback(() => stateRef.current, []);

  return [state, setState, getState];
}

使用useGetState

const MyComponent = () => {
  const [count, setCount, getCount] = useGetState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      console.log('interval count', getCount());
    }, 3000);

    return () => clearInterval(interval);
  }, [getCount]);

  return <button onClick={() => setCount(c => c + 1)}>count: {count}</button>;
};

useUpdateEffect

useUpdateEffect是一个只在依赖项更新时执行的useEffect Hook。这对于忽略组件的初始挂载而只在更新时运行副作用非常有用。下面是如何实现它的代码:

import { useEffect, useRef } from 'react';

function useUpdateEffect(effect, deps) {
  const isInitialMount = useRef(true);

  useEffect(() => {
    if (isInitialMount.current) {
      isInitialMount.current = false;
    } else {
      return effect();
    }
  }, deps);
}

使用useUpdateEffect

const MyComponent = () => {
  const [count, setCount] = useState(0);

  useUpdateEffect(() => {
    // 这里的代码只会在count更新时执行,而不是在组件挂载时
    console.log('count updated:', count);
  }, [count]);

  return <button onClick={() => setCount(c => c + 1)}>count: {count}</button>;
};

useSafeState

useSafeState 是一个自定义的 React Hook,用于确保在组件卸载后不会执行状态更新操作,从而防止内存泄漏或其他相关错误。这个Hook通常通过使用一个布尔型的 ref 来跟踪组件的挂载状态。

import { useState, useRef, useEffect, useCallback } from 'react';

function useSafeState(initialState) {
  const [state, setState] = useState(initialState);
  const isMountedRef = useRef(true);

  useEffect(() => {
    // 组件挂载时不需要做什么
    return () => {
      // 组件卸载时,设置标志
      isMountedRef.current = false;
    };
  }, []);

  // 更新状态的安全版本
  const setSafeState = useCallback((value) => {
    // 如果组件已经卸载,则不进行状态更新
    if (isMountedRef.current) {
      setState(value);
    }
  }, []);

  return [state, setSafeState];
}

export default useSafeState;

useLastState

useLastState 是一个自定义的 React Hook,其目的是在组件的整个生命周期内保留最后一次状态更新的值。即使组件重新渲染,最后的状态值也会被保留。这个 Hook 可以通过结合 useState 和 useRef 钩子来实现。

import { useState, useRef, useEffect } from 'react';

function useLastState(initialValue) {
  const [state, setState] = useState(initialValue);
  const lastStateRef = useRef(initialValue);

  useEffect(() => {
    // 每次state变化时,更新lastStateRef.current的值
    lastStateRef.current = state;
  }, [state]);

  // 获取上一状态的函数
  const getLastState = () => lastStateRef.current;

  return [state, setState, getLastState];
}

export default useLastState;

结语

通过手写ahooks中的部分Hook,我们不仅可以加深对它们工作原理的理解,还能够更灵活地在项目中根据具体需求来定制Hook。实践是检验真理的唯一标准,当你在实际项目中应用这些知识时,你会发现更多的学习和成长机会。


A Student on the way to full stack of Web3.