useControllableValue:管理组件受控非受控状态
- 受控组件:值状态完全由外部父组件管理,控制权在调用的父组件上
- 非受控组件:值状态由内部子组件管理
- hooks 传参:props -
```typescript
export interface Options
{ defaultValue?: T; defaultValuePropName?: string; valuePropName?: string; trigger?: string; }
export type Props = Record
export interface StandardProps
function useControllableValue
const value = props[valuePropName] as T; const isControlled = valuePropName in props;
const initialValue = useMemo(() => { if (isControlled) { return value; } if (defaultValuePropName in props) { return props[defaultValuePropName]; } return defaultValue; }, []);
const stateRef = useRef(initialValue); if (isControlled) { stateRef.current = value; }
const update = useUpdate();
const setState = (v: T, …args: any[]) => { if (!isControlled) { stateRef.current = v; update(); } if (props[trigger]) { propstrigger; } };
return [stateRef.current, useMemoizedFn(setState)] as const; }
<a name="c838N"></a>
### useCreation:useMemo 或 useRef 替代品,保证创建出来的常量,不会被重复创建
useCreation 存在的意义<br />![截屏2022-01-07 下午6.14.52.png](https://cdn.nlark.com/yuque/0/2022/png/22895623/1641550545678-396d14df-4e1c-4601-9d9a-f5c5c0e16fc4.png#clientId=u3091cd92-19f5-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u85dddef7&margin=%5Bobject%20Object%5D&name=%E6%88%AA%E5%B1%8F2022-01-07%20%E4%B8%8B%E5%8D%886.14.52.png&originHeight=874&originWidth=2548&originalType=binary&ratio=1&rotation=0&showTitle=false&size=303580&status=done&style=none&taskId=u4e2a5cc6-0e37-49e8-bef4-b8acdbb174a&title=)<br />useMemo:<br />![截屏2022-01-07 下午6.36.59.png](https://cdn.nlark.com/yuque/0/2022/png/22895623/1641551858227-6cfa845f-adfc-45fb-a944-5b24c1874f3f.png#clientId=u3091cd92-19f5-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u1c501b5a&margin=%5Bobject%20Object%5D&name=%E6%88%AA%E5%B1%8F2022-01-07%20%E4%B8%8B%E5%8D%886.36.59.png&originHeight=310&originWidth=1592&originalType=binary&ratio=1&rotation=0&showTitle=false&size=98333&status=done&style=none&taskId=u46717fbd-856a-4aef-9dc2-c93e1093c16&title=)<br />源码实现:
- 本质还是使用 useRef,将 factory 函数执行的结果保存在 current.obj 内
- 使用 initialized 标记是不是已经初始化过
```typescript
import type { DependencyList } from 'react';
import { useRef } from 'react';
import depsAreSame from '../utils/depsAreSame';
export default function useCreation<T>(factory: () => T, deps: DependencyList) {
const { current } = useRef({
deps,
obj: undefined as undefined | T,
initialized: false,
});
// 只有 initialized 是 false
// 或者 依赖列表发生变化的时候
// 才需要重新执行 factory(), 获取新的实例
if (current.initialized === false || !depsAreSame(current.deps, deps)) {
current.deps = deps;
current.obj = factory();
current.initialized = true;
}
// 返回这个实例
return current.obj as T;
}
useEventEmitter:事件分发
import { useRef, useEffect } from 'react';
type Subscription<T> = (val: T) => void;
export class EventEmitter<T> {
// 事件池子
private subscriptions = new Set<Subscription<T>>();
// 事件派发
emit = (val: T) => {
for (const subscription of this.subscriptions) {
subscription(val);
}
};
// 事件订阅
useSubscription = (callback: Subscription<T>) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const callbackRef = useRef<Subscription<T>>();
callbackRef.current = callback;
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
function subscription(val: T) {
if (callbackRef.current) {
callbackRef.current(val);
}
}
this.subscriptions.add(subscription);
return () => {
this.subscriptions.delete(subscription);
};
}, []);
};
}
export default function useEventEmitter<T = void>() {
const ref = useRef<EventEmitter<T>>();
// 保证全局下只生成一个 EventEmitter 实例
if (!ref.current) {
ref.current = new EventEmitter();
}
return ref.current;
}
如何保证全局只有一个 EventEmitter 实例,通过 ref.current 去保存,如果说 ref.current 已经赋值过,则不会再重新 new 生成实例
const ref = useRef<EventEmitter<T>>();
// 保证全局下只生成一个 EventEmitter 实例
if (!ref.current) {
ref.current = new EventEmitter();
}
return ref.current;
EventEmitter Class 整体结构 ```typescript type Subscription
= (val: T) => void;
export class EventEmitter
// 事件分发 emit = (val: T) => { // … }
// 事件订阅,是个 hooks
useSubscription = (callback: Subscription
- EventEmitter,emit 方法
```typescript
// 遍历 subscriptions 回调事件,传值循环执行
emit = (val: T) => {
for (const subscription of this.subscriptions) {
subscription(val);
}
};
EventEmitter,useSubscription 方法,使用 useRef 保存 callback,变更 .current 不会导致组件重复渲染。在初始化时,对 callback 进行包装,注册到 subscription Set 事件数组内,当组件注销时,会在 subscription Set 内删除对应的事件
useSubscription = (callback: Subscription<T>) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
const callbackRef = useRef<Subscription<T>>();
callbackRef.current = callback;
// eslint-disable-next-line react-hooks/rules-of-hooks
useEffect(() => {
function subscription(val: T) {
if (callbackRef.current) {
callbackRef.current(val);
}
}
this.subscriptions.add(subscription);
return () => {
this.subscriptions.delete(subscription);
};
}, []);
};
useIsomorphicLayoutEffect:同构 useLayoutEffect 的 hooks,解决 SSR 问题
useLayoutEffect:DOM 变更后同步调用的 effect
- useEffect:在每轮渲染完后执行的 effect
- useLayouEffect 无法在 SSR 环境内使用,需要用 useEffect 替换
// 判断是否在浏览器端,是的话则使用 useLayoutEffect,否则使用 useEffect
const useIsomorphicLayoutEffect = isBrowser ? useLayoutEffect : useEffect;
useLastest:返回当前最新值的 Hook,避免闭包问题
```javascript import { useRef } from ‘react’;
// 使用 useRef,在整个生命周期内值保持不变
function useLatest
return ref; }
export default useLatest;
<a name="aE2VC"></a>
### useMemoizedFn:持久化 function
```typescript
type noop = (...args: any[]) => any;
function useMemoizedFn<T extends noop>(fn: T) {
if (process.env.NODE_ENV === 'development') {
if (typeof fn !== 'function') {
console.error(`useMemoizedFn expected parameter is a function, got ${typeof fn}`);
}
}
const fnRef = useRef<T>(fn);
// why not write `fnRef.current = fn`?
// https://github.com/alibaba/hooks/issues/728
fnRef.current = useMemo(() => fn, [fn]);
const memoizedFn = useRef<T>();
if (!memoizedFn.current) {
memoizedFn.current = function (...args) {
// eslint-disable-next-line @typescript-eslint/no-invalid-this
return fnRef.current.apply(this, args);
} as T;
}
return memoizedFn.current;
}
useReactive:新的数据响应方式,直接修改值即可
```typescript
// k:v 原对象:代理过的对象 const proxyMap = new WeakMap(); // k:v 代理过的对象:原对象 const rawMap = new WeakMap();
function isObject(val: Record
function observer
// 添加缓存 防止重新构建proxy if (existingProxy) { return existingProxy; }
// 防止代理已经代理过的对象 // https://github.com/alibaba/hooks/issues/839 if (rawMap.has(initialVal)) { return initialVal; }
const proxy = new Proxy
proxyMap.set(initialVal, proxy); rawMap.set(proxy, initialVal);
return proxy; }
function useReactive>(initialState: S): S {
const update = useUpdate();
const stateRef = useRef(initialState);
const state = useCreation(() => { return observer(stateRef.current, () => { update(); }); }, []);
return state; } ```