customHook 自定义事件

799 阅读2分钟

useEventListener 事件监听的 customHook

  • 定义custom hook时名字必须以use开头,否则eslint会报错,因为不管是自己定义的hook还是react自带的hook都只能在组件中或其他hook中运行, 需要在顶层调用
const useEventListener(eventName, handler, element = window) => {

    // 创建一个存储处理程序的引用
    const savedHandler = useRef();

    // 如果依赖 [handler] 发生更改,则更新ref.current值.
    // 这使得我们下面的效果总是得到最新的处理程序
    useEffect(() => {
      savedHandler.current = handler;
    }, [handler]);

    useEffect(
      () => {
        const isSupported = element && element.addEventListener;
        if (!isSupported) return;

        // 创建调用ref中存储的处理程序函数的事件侦听器
        const eventListener = event => savedHandler.current(event);

        // Add 监听事件
        element.addEventListener(eventName, eventListener);

        // 销毁时移除监听
        return () => {
          element.removeEventListener(eventName, eventListener);
        };
      },
      [eventName, element], 
    );
}

useEventListener('click', function() {
    // todo something
})

useMount 的 customHook 封装useEffect

export const useMount = (callback) => {
    useEffect(() => {
      callback()
    },[])
}

// 只在页面渲染时执行一次获取data
useMount(() => {
    fetch(`${Url}/data`).then(async response => {
        if (response.ok) {
            setUsers(await response.json())
        }
    })
})

useComponentWillMount 自定义hook 类组件生命周期 ComponentWillMount

const useComponentWillMount = func => {
  const willMount = useRef(true);

  if (willMount.current) {
    func();
  }

  willMount.current = false;
};

useDebounce 的 customHook, 多用在 input 搜索框的调用API

  • 定义搜索框防抖debounce,只会执行最后的定时任务
  • 节流是同一时间只能执行一个任务,执行完毕后才能执行下一个,原理是定义一个状态,在执行任务前后改变状态,执行体通过判断此状态可以判断该函数是否在执行,从而决定是否执行
多用在 input 搜索框

// 防抖示例
export const debounce = (func, delay) => {
    let timeout
    return ()=> {
        if(timeout){
            clearTimeout(timeout)
        }
        timeout = setTimeout(() => {
            func()
        }, delay);
    }
}
export const useDebounce = (value, delay) => {
    const [debounceValue, setDebounceValue] = useState(value)
    
    // 每当输入框传入的value变化时,Effect hook就会设置一个定时器,
    // 在delay时间后操作state hook更新从外界传入的debounceVlue中的value值,
    // 当此定时器执行完毕后 Effect再清理定时器 最后将处理后的value return出去
    useEffect(() => {
        const timeout = setTimeout(() => setDebounceValue(value), delay)
        return () => clearTimeout(timeout)
    }, [value, delay])
    
    return debounceValue
}

每次value更新,就会重新触发下一个useEffect,这时就会执行return里的函数,
意思就是每个useEffect触发时,将会清理掉上一个useEffect定义的定时器
例子:请求 API 时

const debounceParam = useDebounce(param, 2000)
useEffect(() => {
    //请求接口
    fetch(`${api}/user?${qs.stringify(debounceParam)}`)
        .then(async response => {
            if (response.ok) {
                // json()返回一个为JSON格式的promise对象
                setList(await response.json())
            }
        })
    }, [debounceParam])


自定义hook  useData 获取API数据,异步获取数据的封装

// 调用
const [ data, setQuery, isLoading ] = useData(url)

function useData(url, options = {}) {
  const [query, setQuery] = useState({});
  const [ data, setData ] = useState(null);
  const [isLoading, setLoading ] = useState(false);
  const [isError, setError ] = useState(false)
  useEffect( () => {
    async function fetchData(){
      try{
        let res = await ApiConfig.usual.useData(url,query)
        console.log(res)
        if(res.status == '200'){
          setData(res.data)
        }
      } catch(err){
        console.log(err)
        setError(true)
      }
      setLoading(false)
    }
    setLoading(true)
    fetchData()
  }, [url,query])
  return [ data, setQuery, isLoading, isError ]
}

useEffect中请求API,可以主动中断请求

  • abortController = new AbortController(),主动的方式是探知到卸载时直接中断请求,不必再收到响应时进行判断
  • AbortController 是一个浏览器的实验接口,它可以返回一个信号量(singal),从而中止发送的请求
  • 链接 :技术来源CSDN
useEffect(() => {
    let isUnmounted = false;
    const abortController = new AbortController(); // 创建
     (async () => {
        const res = await fetch(SOME_API, {
            singal: abortController.singal, // 当做信号量传入
        });
        const data = await res.json();
        if (!isUnmounted) {
            setValue(data.value);
            setLoading(false);
        }
    })();

    return () => {
        isUnmounted = true;
        abortController.abort(); // 在组件卸载时中断
    }
}, []);

结语

前端react QQ群:788023830 ---- React/Redux - 地下老英雄

前端交流 QQ群:249620372 ---- FRONT-END-JS前端

(我们的宗旨是,为了加班,为了秃顶……,仰望大佬),希望小伙伴们加群一起学习