1、useContextReducer
实现:
//先创建一个context 传递一个数组,这个数组中有两个元素,一个是对象,一个是回调函数
const Provider=({children})=>{
const state=React.useState({});
const value=React.useMemo(()=>state,[state[0]])
return <Context.Provider value={value}>{children}</Context.Provider>
}
function useContextReducer(contextKey,reducer,initalState,initalAction){
const [contextState,setContextState]=useContext(Context);
let [state]=useReducer(reducer,initalState,initalAction);
if(contextState[contextKey]!=null){
state=contextState[contextKey]
}
const dispatch=action=>{
setContextState(preState=>{
Object.assign({},preState,{
[contextKey]:reducer(preState[contextKey],action)
})
})
}
useEffect(()=>{
if(contextState[contentKey]==null && state !=null){
setContextState(preState=>{
if(preState[contextKey]==null){
return Object.assign({},preState,{
[contentKey]:state
})
}
return preState;
})
}
},[contextKey])
return [state,dispatch];
}
案例:
function reducer(state, action) {
switch (action.type) {
case "INCREMENT":
return state + 1;
default:
return state;
}
}
function useCounter() {
const [count, dispatch] = useContextReducer("counter", reducer, 0);
const increment = () => dispatch({ type: "INCREMENT" });
return { count, increment };
}
function IncrementButton() {
const { increment } = useCounter();
return <button onClick={increment}>Increment</button>;
}
function Count() {
const { count } = useCounter();
return <div>{count}</div>;
}
function Demo() {
return (
<Provider>
<Count />
<IncrementButton />
</Provider>
);
}
2、useContextState
实现:
const Context = React.createContext([{}, () => {}]);
const Provider = ({ children }) => {
const state = React.useState({});
const value = React.useMemo(() => state, [state[0]]);
return <Context.Provider value={value}>{children}</Context.Provider>;
};
function useContextState(contextKey, initialState) {
const [contextState, setContextState] = React.useContext(Context);
const state =
contextState[contextKey] != null ? contextState[contextKey] : initialState;
const setState = nextState =>
setContextState(prevState =>
Object.assign({}, prevState, {
[contextKey]:
typeof nextState === "function" ? nextState(prevState) : nextState
})
);
React.useEffect(() => {
if (contextState[contextKey] == null && state != null) {
setContextState(prevState => {
if (prevState[contextKey] == null) {
return Object.assign({}, prevState, {
[contextKey]: state
});
}
return prevState;
});
}
}, [contextKey]);
return [state, setState];
}
案例:
function useCounter() {
const [count, setCount] = useContextState("counter", 0);
const increment = () => setCount(count + 1);
return { count, increment };
}
function IncrementButton() {
const { increment } = useCounter();
return <button onClick={increment}>Increment</button>;
}
function Count() {
const { count } = useCounter();
return <div>{count}</div>;
}
function Demo() {
return (
<Provider>
<Count />
<IncrementButton />
</Provider>
);
}
3、useArray
实现:
const useArray = initial => {
const [value, setValue] = useState(initial);
return {
value,
setValue,
add: useCallback(a => setValue(v => [...v, a]), []),
clear: useCallback(() => setValue(() => []), []),
removeById: useCallback(
id => setValue(arr => arr.filter(v => v && v.id !== id)),
[]
),
removeIndex: useCallback(
index =>
setValue(v => {
v.splice(index, 1);
return v;
}),
[]
)
};
};
案例:
function Demo() {
const todos = useArray(["Item 1"]);
return todos.value.map(todo => <div>{todo}</div>);
}
4、useBoolean
实现:
const useBoolean = initial => {
const [value, setValue] = useState(initial);
return {
value,
setValue,
toggle: useCallback(() => setValue(v => !v), []),
setTrue: useCallback(() => setValue(true), []),
setFalse: useCallback(() => setValue(false), [])
};
};
案例:
function Demo() {
const toggle = useBoolean(false);
return toggle.value ? "On" : "Off";
}
5、useInput
实现:
const useInput = initial => {
const isNumber = typeof initial === "number";
const [value, setValue] = useState(initial);
const onChange = useCallback(e => setValue(e.target.value), []);
return {
value,
setValue,
hasValue:
value !== undefined &&
value !== null &&
(!isNumber ? value.trim && value.trim() !== "" : true),
clear: useCallback(() => setValue(""), []),
onChange,
bindToInput: {
onChange,
value
},
bind: {
onChange: setValue,
value
}
};
};
案例:
function Demo() {
const newTodo = useInput("");
return (
<input type="text" value={newTodo.value} onChange={newTodo.onChange} />
);
}
6、useOnMount
实现:
const useOnMount = onMount =>
useEffect(() => {
onMount && onMount();
}, []);
案例:
function Demo() {
useOnMount(() => console.log("hello world"));
return <div />;
}
7、useOnUnMount
实现:
const useOnUnmount = onUnmount =>
useEffect(() => {
return () => onUnmount && onUnmount();
}, []);
案例:
function Demo() {
// replace `<div></div>` below with `null` to see output in console
useOnUnmount(() => console.log("goodbye world"));
return <div />;
}
8、useActive
实现:
function useActive({ onChange, refEl }) {
const [value, setValue] = useState(false);
const handleMouseDown = () => {
setValue(true);
onChange(true);
};
const handleMouseUp = () => {
setValue(false);
onChange(false);
};
useEffect(() => {
if (refEl && refEl.current) {
refEl.current.addEventListener("mousedown", handleMouseDown);
refEl.current.addEventListener("mouseup", handleMouseUp);
}
return () => {
if (refEl && refEl.current) {
refEl.current.removeEventListener("mousedown", handleMouseDown);
refEl.current.removeEventListener("mouseup", handleMouseUp);
}
};
}, []);
return value;
}
案例:
function Demo() {
const inputEl = useRef(null);
const activeValue = useActive({ refEl: inputEl });
return (
<>
<input ref={inputEl} type="text" />
<div>{activeValue ? "Active" : "Nop"}</div>
</>
);
}
9、useInterval
实现:
function useInterval({ startImmediate, duration, callback }) {
const [count, updateCount] = useState(0);
const [intervalState, setIntervalState] = useState(
startImmediate === undefined ? true : startImmediate
);
const [intervalId, setIntervalId] = useState(null);
useEffect(() => {
if (intervalState) {
const intervalId = setInterval(() => {
updateCount(count + 1);
callback && callback();
}, duration);
setIntervalId(intervalId);
}
return () => {
if (intervalId) {
clearInterval(intervalId);
setIntervalId(null);
}
};
}, [intervalState, count]);
return {
intervalId,
start: () => {
setIntervalState(true);
},
stop: () => {
setIntervalState(false);
}
};
}
案例:
function Demo() {
const [time, setTime] = useState(null);
const { start, stop } = useInterval({
duration: 1000,
startImmediate: false,
callback: () => {
setTime(new Date().toLocaleTimeString());
}
});
return (
<>
<div>The time is now {time}</div>
<button onClick={() => stop()}>Stop interval</button>
<button onClick={() => start()}>Start interval</button>
</>
);
}
10、useMap
实现:
function useMap(initial) {
const [mapValue, setMapValue] = useState(() => initial);
return {
values: mapValue,
clear: () => setMapValue({}),
reset: () => setMapValue(initial),
set: (key, updater) => {
setMapValue(prev =>
Object.assign(prev, {
[key]: typeof updater === "function" ? updater(prev[key]) : updater
})
);
},
get: key => mapValue[key],
has: key => mapValue[key] != null,
delete: key => setMapValue(({ [key]: deleted, ...prev }) => prev)
};
}
案例:
function Demo() {
const {
set: setKey,
get: getKey,
has,
delete: deleteKey,
clear,
reset,
values
} = useMap({ name: "PK", age: "30", occupation: "Reactor" });
return JSON.stringify(values);
}
11、useToggle
实现:
const useToggle = initialValue => {
const [value, setValue] = useState(initialValue);
const toggler = useCallback(() => setValue(value => !value));
return [value, toggler];
};
案例:
function Demo() {
const [currentValue, toggleAway] = useToggle(true);
return <div onClick={toggleAway}>{currentValue ? "🍎" : "🍏"}</div>;
}
12、useAsync
实现:
const useAsync = (fn, args) => {
const [state, set] = useState({
loading: true
});
const memoized = useCallback(fn, args);
useEffect(() => {
let mounted = true;
const promise = memoized();
promise.then(
value => {
if (mounted) {
set({
loading: false,
value
});
}
},
error => {
if (mounted) {
set({
loading: false,
error
});
}
}
);
return () => {
mounted = false;
};
}, [memoized]);
return state;
};
案例:
// Returns a Promise that resolves after one second.
const fn = () =>
new Promise(resolve => {
setTimeout(() => {
resolve("RESOLVED");
}, 1000);
});
function Demo() {
const { loading, value, error } = useAsync(fn);
return (
<div>{loading ? <div>Loading...</div> : <div>Value: {value}</div>}</div>
);
}
13、useRaf
React动画钩子,强制组件在每个reaquestAnimationFrame上重新渲染,返回时间流逝的百分比
实现:
const useRaf = (ms = 1e12, delay = 0) => {
const [elapsed, set] = useState(0);
useEffect(() => {
let raf, timerStop, start;
const onFrame = () => {
const time = Math.min(1, (Date.now() - start) / ms);
set(time);
loop();
};
const loop = () => {
raf = requestAnimationFrame(onFrame);
};
const onStart = () => {
timerStop = setTimeout(() => {
cancelAnimationFrame(raf);
set(1);
}, ms);
start = Date.now();
loop();
};
const timerDelay = setTimeout(onStart, delay);
return () => {
clearTimeout(timerStop);
clearTimeout(timerDelay);
cancelAnimationFrame(raf);
};
}, [ms, delay]);
return elapsed;
};
案例:
function Demo() {
const elapsed = useRaf(5000, 1000);
return <div>Elapsed: {elapsed}</div>;
}
14、useTimeout
在指定的毫秒数后返回true
实现:
const useTimeout = (ms = 0) => {
const [ready, setReady] = useState(false);
useEffect(() => {
let timer = setTimeout(() => {
setReady(true);
}, ms);
return () => {
clearTimeout(timer);
};
}, [ms]);
return ready;
};
案例:
function Demo() {
const ready = useTimeout(2000);
return <div>Ready: {ready ? "Yes" : "No"}</div>;
}
15、useLocalStorage
实现:
function useLocalStorage(key) {
let localStorageItem;
if (key) {
localStorageItem = localStorage[key];
}
const [localState, updateLocalState] = useState(localStorageItem);
function syncLocalStorage(event) {
if (event.key === key) {
updateLocalState(event.newValue);
}
}
useEffect(() => {
window.addEventListener("storage", syncLocalStorage);
return () => {
window.removeEventListener("storage", syncLocalStorage);
};
}, []);
return localState;
}
案例:
function Demo() {
let name = useLocalStorage("name"); // send the key to be tracked.
return (
<div>
<h1>{name}</h1>
</div>
);
}
16、useNetworkStatus
实现:
function getConnection() {
return (
navigator.connection ||
navigator.mozConnection ||
navigator.webkitConnection
);
}
function useNetworkStatus() {
let [connection, updateNetworkConnection] = useState(getConnection());
function updateConnectionStatus() {
updateNetworkConnection(getConnection());
}
useEffect(() => {
connection.addEventListener("change", updateConnectionStatus);
return () => {
connection.removeEventListener("change", updateConnectionStatus);
};
}, []);
return connection;
}
案例:
function Demo() {
let connection = useNetworkStatus();
return (
<div>
<div>downlink: {connection.downlink}</div>
<div>effectiveType: {connection.effectiveType}</div>
<div>rtt: {connection.rtt}</div>
<div>saveData: {connection.saveData ? "yes" : "no"}</div>
</div>
);
}
17、useOnlineStatus
实现:
function getOnlineStatus() {
return typeof navigator !== "undefined" &&
typeof navigator.onLine === "boolean"
? navigator.onLine
: true;
}
function useOnlineStatus() {
let [onlineStatus, setOnlineStatus] = useState(getOnlineStatus());
function goOnline() {
setOnlineStatus(true);
}
function goOffline() {
setOnlineStatus(false);
}
useEffect(() => {
window.addEventListener("online", goOnline);
window.addEventListener("offline", goOffline);
return () => {
window.removeEventListener("online", goOnline);
window.removeEventListener("offline", goOffline);
};
}, []);
return onlineStatus;
}
案例:
function Demo() {
let onlineStatus = useOnlineStatus();
return (
<div>
<h1>You are {onlineStatus ? "Online" : "Offline"}</h1>
</div>
);
}
18、useComponentSize
获取组件的实际大小
实现:
function getSize(el) {
if (!el) {
return {};
}
return {
width: el.offsetWidth,
height: el.offsetHeight
};
}
function useComponentSize(ref) {
let [ComponentSize, setComponentSize] = useState(getSize(ref.current));
function handleResize() {
if (ref && ref.current) {
setComponentSize(getSize(ref.current));
}
}
useLayoutEffect(() => {
handleResize();
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return ComponentSize;
}
案例:
function Demo() {
let ref = useRef(null);
let size = useComponentSize(ref);
// size == { width: 100, height: 200 }
let { width, height } = size;
let imgUrl = `https://via.placeholder.com/${width}x${height}`;
return (
<div ref={ref} style={{ width: "100%", height: "100%" }}>
<img src={imgUrl} />
</div>
);
}
18、useWindowMousePosition
获取鼠标在全局中的位置
实现:
function useWindowMousePosition() {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
function handleMouseMove(e) {
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
});
return WindowMousePosition;
}
案例:
function Demo() {
let { x, y } = useWindowMousePosition();
return (
<div style={{ width: "100%", height: "100%" }}>
<pre>{JSON.stringify({ x, y })}</pre>
</div>
);
}
19、useWindowSize
实现:
function getSize() {
return {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth,
outerHeight: window.outerHeight,
outerWidth: window.outerWidth
};
}
function useWindowSize() {
let [windowSize, setWindowSize] = useState(getSize());
function handleResize() {
setWindowSize(getSize());
}
useEffect(() => {
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return windowSize;
}
案例:
function Demo() {
const size = useWindowSize();
return <div>{size.innerWidth}</div>;
}
20、useWindowScrollPosition
滚动条的滚动位置
实现:
// We need to import lodash throttle if we want to throttle our scroll events
// import throttle from 'lodash.throttle';
const useWindowScrollPosition = (options = {}) => {
const { throttleMs = 100 } = options;
const [scroll, setScroll] = React.useState({
x: window.pageXOffset,
y: window.pageYOffset
});
const handle = throttle(() => {
setScroll({
x: window.pageXOffset,
y: window.pageYOffset
});
}, throttleMs);
React.useEffect(() => {
window.addEventListener("scroll", handle);
return () => {
window.removeEventListener("scroll", handle);
};
}, []);
return scroll;
};
案例:
function Demo() {
const { x, y } = useWindowScrollPosition();
return (
<div style={{ width: "100%", height: "100%" }}>
<pre>{JSON.stringify({ x, y })}</pre>
</div>
);
}
21、useIntersectionObserver
IntersectionObserver提供了一种异步观察目标元素与其祖先元素或顶级文档视窗(viewport)交叉状态的方法。祖先元素与视窗(viewport)被称为根(root)。
当一个IntersectionObserver对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦IntersectionObserver被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
实现:
const useIntersectionObserver = (target, root) => {
const [isIntersecting, setIntersecting] = React.useState(false);
React.useEffect(() => {
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting !== isIntersecting) {
setIntersecting(entry.isIntersecting);
}
},
{
rootMargin: "0px",
root: root.current
}
);
if (target.current) {
observer.observe(target.current);
}
return () => {
observer.unobserve(target.current);
};
}, []);
return isIntersecting;
};
案例:
function Demo() {
const targetRef = useRef(null);
const isIntersecting = useIntersectionObserver(
targetRef,
document.querySelector("body")
);
return (
<div className="App" ref={targetRef}>
<h2>{isIntersecting ? "Component is visible" : "Component is hidden"}</h2>
</div>
);
}
22、usePromise
简化了React组件内部的promise处理
实现:
const usePromise = (fn, { resolve = false, resolveCondition = [] } = {}) => {
const [data, setData] = useState();
const [isLoading, setLoading] = useState(resolve);
const [lastUpdated, setLastUpdated] = useState();
const [error, setError] = useState();
const request = (...args) => {
/*
Using isValid guard, in order to prevent the cleanup warning.
*/
let isValid = true;
setLoading(true);
fn(...args)
.then(result => {
if (!isValid) return;
setData(result);
setLastUpdated(Date.now());
})
.catch(err => {
if (!isValid) return;
setError(err);
})
.finally(() => {
if (!isValid) return;
setLoading(false);
});
/*
When component will be unmounted, isValid will become false and state setter
functions will not be envoked on unmounted component.
*/
return () => {
isValid = false;
};
};
if (resolve) {
useEffect(request, resolveCondition);
}
return {
request,
data,
isLoading,
lastUpdated,
error
};
};
案例:
const Demo = () => {
const { isLoading, data } = usePromise(fetchMovies, { resolve: true });
return isLoading ? (
<div>Loading...</div>
) : (
<ul>
{data.map(movie => (
<li key={movie.id}>{movie.title}</li>
))}
</ul>
);
};
const fetchMovies = () =>
new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{ id: 1, title: "The Godfather" },
{ id: 2, title: "The Dark Knight" },
{ id: 3, title: "Fight Club" }
]);
}, 1000);
});
23、useThrottle
实现:
const useThrottle = (value, limit) => {
const [throttledValue, setThrottledValue] = useState(value);
const lastRan = useRef(Date.now());
useEffect(() => {
const handler = setTimeout(function() {
if (Date.now() - lastRan.current >= limit) {
setThrottledValue(value);
lastRan.current = Date.now();
}
}, limit - (Date.now() - lastRan.current));
return () => {
clearTimeout(handler);
};
}, [value, limit]);
return throttledValue;
};
案例:
function Demo() {
const [text, setText] = useState("Hello");
const throttledText = useThrottle(text, 1000);
return (
<div>
<input
defaultValue={"Hello"}
onChange={e => {
setText(e.target.value);
}}
/>
<p>Actual value: {text}</p>
<p>Throttled value: {throttledText}</p>
</div>
);
}
24、useDraggable
实现:
function useDraggable(el) {
const [{ dx, dy }, setOffset] = useState({ dx: 0, dy: 0 });
useEffect(() => {
const handleMouseDown = event => {
const startX = event.pageX - dx;
const startY = event.pageY - dy;
const handleMouseMove = event => {
const newDx = event.pageX - startX;
const newDy = event.pageY - startY;
setOffset({ dx: newDx, dy: newDy });
};
document.addEventListener("mousemove", handleMouseMove);
document.addEventListener(
"mouseup",
() => {
document.removeEventListener("mousemove", handleMouseMove);
},
{ once: true }
);
};
el.current.addEventListener("mousedown", handleMouseDown);
return () => {
el.current.removeEventListener("mousedown", handleMouseDown);
};
}, [dx, dy]);
useEffect(() => {
el.current.style.transform = `translate3d(${dx}px, ${dy}px, 0)`;
}, [dx, dy]);
}
案例:
function Demo() {
const el = useRef();
useDraggable(el);
const style = {
border: "3px solid cyan",
margin: "40px auto 0 auto",
width: "100px",
height: "100px"
};
return <div ref={el} style={style} />;
}
25、useFoucs
实现:
const useFocus = (ref, defaultState = false) => {
const [state, setState] = useState(defaultState);
useEffect(() => {
const onFocus = () => setState(true);
const onBlur = () => setState(false);
ref.current.addEventListener("focus", onFocus);
ref.current.addEventListener("blur", onBlur);
return () => {
ref.current.removeEventListener("focus", onFocus);
ref.current.removeEventListener("blur", onBlur);
};
}, []);
return state;
};
案例:
function Demo() {
const ref = useRef();
const focused = useFocus(ref);
return (
<div className={`form-field${focused && " is-focused"}`}>
<input type="text" ref={ref} placeholder="focus on me" />
{focused && (
<span className="tip">
This trivial example could easily be achieved with{" "}
<code>input:focus + .tip</code> but managing styles on a parent
element is where this hook shines.
</span>
)}
</div>
);
}