- 以 “use” 开头、 提炼复用逻辑
- 在函数最外层调⽤ Hook、且只能在函数组件中使用
自定义 useState
import { useEffect, useRef, useState } from 'react'
const useXState = (initState) => {
const [state, setState] = useState(initState);
let isUpdate = useRef()
const setXState = (state, cb) => {
setState(prev => {
isUpdate.current = cb
return typeof state === 'function' ? state(prev) : state
})
}
useEffect(() => {
if(isUpdate.current) {
isUpdate.current()
}
})
return [state, setXState]
}
export default useXState
自定义 定时器
// ⾃定义hook - 定制器
function useClock() {
const [date, setDate] = useState(new Date());
useEffect(() => {
// didMount时执行
const timer = setInterval(() => {
setDate(new Date());
}, 1000);
// 清除定时器,类似willUnmount
return () => clearInterval(timer);
}, []);
return date;
}
// 使用⾃定义hook
import React, { useState, useEffect, useMemo } from "react";
export default function CustomHookPage(props) {
const [count, setCount] = useState(0);
// 副作用函数
useEffect(() => {
document.title = `点击了${count}次`;
}, [count]); // 依赖项 count
return (
<div>
<p>{count}</p>
<button onClick={() => setCount(count + 1)}>add</button>
<p>{useClock().toLocaleTimeString()}</p>
</div>
);
}
自定义 动态切换标题
// ⾃定义hook - title
import { useEffect } from 'react'
const useTitle = (title) => {
useEffect(() => {
document.title = title
}, [])
return
}
export default useTitle
// 使用⾃定义hook
const Home = () => {
useTitle('趣谈前端')
return <div>home</div>
}
自定义 更新组件( forceUpdate )
// ⾃定义hook - forceUpdate
import { useState } from 'react'
const useUpdate = () => {
const [, setFlag] = useState()
const update = () => {
setFlag(Date.now())
}
return update
}
export default useUpdate
// 使用⾃定义hook
const Home = (props) => {
const update = useUpdate()
return (
<div>
{Date.now()}
<div><button onClick={update}>update</button></div>
</div>
)
}
自定义 监听窗口
// ⾃定义hook - 监听窗口
import React, { useState, useCallback, useEffect } from "react";
export const useWinSize = () => {
const [size, setSize] = useState({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
});
const changeSize = useCallback(() => {
setSize({
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight
});
}, []);
useEffect(() => {
window.addEventListener("resize", changeSize);
return () => {
window.removeEventListener("resize", changeSize);
};
}, []);
return size;
};
// 使用⾃定义hook
import React, { useRef } from 'react'
import { useWinSize } from "./customHook/useWinSize";
function App(props) {
const size = useWinSize();
return (
<div >
页面大小: `{size.width}*{size.height}`
</div>
)
}
export default App;
自定义 节流防抖
// 自定义Hook - 节流
import { useEffect, useRef, useState } from 'react'
const useThrottle = (fn, ms = 30, deps = []) => {
let previous = useRef(0)
let [time, setTime] = useState(ms)
useEffect(() => {
let now = Date.now();
if (now - previous.current > time) {
fn();
previous.current = now;
}
}, deps) // 依赖更新
const cancel = () => {
setTime(0)
}
return [cancel]
}
export default useThrottle
自定义 防抖
// js写法
import { useEffect, useRef } from 'react'
const useDebounce = (fn, ms = 30, deps = []) => {
let timeout = useRef()
useEffect(() => {
if (timeout.current) {
clearTimeout(timeout.current)
timeout.current = null;
}
timeout.current = setTimeout(() => {
fn()
}, ms)
}, deps)
const cancel = () => {
clearTimeout(timeout.current)
timeout.current = null
}
return [cancel]
}
export default useDebounce
// ts 写法
import { useEffect, useRef } from 'react';
export default (fn: () => void, ms = 200, deps: string[] = []) => {
const timeout = useRef<ReturnType<typeof setTimeout> | null>(null);
useEffect(() => {
if (timeout.current) {
clearTimeout(timeout.current);
timeout.current = null;
}
timeout.current = setTimeout(() => {
fn();
}, ms);
}, deps);
const cancel = () => {
clearTimeout(timeout.current as ReturnType<typeof setTimeout>);
timeout.current = null;
};
return [cancel];
};
// 使用⾃定义hook
const Home = (props) => {
const [a, setA] = useState(0)
const [b, setB] = useState(0)
const [cancel] = useDebounce(() => {
setB(a)
}, 2000, [a])
const changeIpt = (e) => {
setA(e.target.value)
}
return (
<div>
{ b } { a }
<input type="text" onChange={changeIpt} />
</div>
)
}