强制Function类型组件刷新
执行reducer的dispatch 也可以使用useState 效果类似
export default function useForceUpdate() {
const [, forceUpdate] = React.useReducer(x => x + 1, 0);
return forceUpdate;
}
检查是否支持 flex的gap样式设定
let flexGapSupported: boolean | undefined;
export const detectFlexGapSupported = () => {
if (!canUseDocElement()) {
return false;
}
if (flexGapSupported !== undefined) {
return flexGapSupported;
}
// create flex container with row-gap set
const flex = document.createElement('div');
flex.style.display = 'flex';
flex.style.flexDirection = 'column';
flex.style.rowGap = '1px';
// create two, elements inside it
flex.appendChild(document.createElement('div'));
flex.appendChild(document.createElement('div'));
// append to the DOM (needed to obtain scrollHeight)
document.body.appendChild(flex);
flexGapSupported = flex.scrollHeight === 1; // flex container should be 1px high from the row-gap
document.body.removeChild(flex);
return flexGapSupported;
};
infer关键字的应用
export const tuple = <T extends string[]>(...args: T) => args;
// eslint-disable-next-line import/prefer-default-export
export const PresetColorTypes = tuple(
'pink',
'red',
'yellow',
);
// T extends xx T属于xx类型吗 判断语句 如果是 E是待推断类型 如果是E[]
// 下面例子中T 是['pink', 'red', 'yellow'] 所以里面的元素类型为 'pink' | 'red' | 'yellow' 所以E='pink' | 'red' | 'yellow'
export type ElementOf<T> = T extends (infer E)[] ? E : T extends readonly (infer F)[] ? F : never;
// 'pink' | 'red' | 'yellow'
export type PresetColorType = ElementOf<typeof PresetColorTypes>;
useSyncState构建同步state
应用场景 表格选中的等keys的处理 等我看完源码来补充
useState不能保证同步 所以自己创建一个同步 使用过程无风险且需要强制更新一次
确保用的时候就是最新的状态量
export default function useSyncState<T>(initialValue: T): UseSyncStateProps<T> {
const ref = React.useRef<T>(initialValue);
const forceUpdate = useForceUpdate();
return [
() => ref.current,
(newValue: T) => {
ref.current = newValue;
// re-render
forceUpdate();
},
];
}
usePatchElement 通过state状态量维护一个数组 完成塞入和有返回值函数可清理自身塞入的元素
export default function usePatchElement(): [
React.ReactElement[],
(element: React.ReactElement) => Function,
] {
const [elements, setElements] = React.useState<React.ReactElement[]>([]);
const patchElement = React.useCallback((element: React.ReactElement) => {
// append a new element to elements (and create a new ref)
setElements(originElements => [...originElements, element]);
// return a function that removes the new element out of elements (and create a new ref)
// it works a little like useEffect
return () => {
setElements(originElements => originElements.filter(ele => ele !== element));
};
}, []);
return [elements, patchElement];
}
wave波浪纹组件开发
https://ant.design/components/button-cn/
常用场景是:按钮点击后 边缘有一圈类似波纹扩散的效果 功能点:
- 希望提供波浪扩展的高阶组件 不能直接修改用户提供的用例组件 比如提供的button,我们不能修改button本身,而是在外层包裹wave包装
- 动态获取按钮button组件的border颜色或者背景颜色用作波浪纹的取色
- button的ref指向原生button,在jsx编译下onClick都会走react事件系统,在原生的button上加一个onClick来触发波浪纹,也就是说click事件触发两套事件处理逻辑,一个是原定的handleClick,一个是原生addEventListener(‘click’, wave)来触发波浪纹效果
- 动画样式控制 透明度 0.2 => 0 时间间隔 2s 动画函数 cubic-bezier(0.08, 0.82, 0.17, 1)
- 动画样式控制 阴影宽度 0 => 6px 时间间隔 0.4s 动画函数 cubic-bezier(0.08, 0.82, 0.17, 1)
- 插入傀儡节点div,相对于目标标签绝对定位,然后 “left:0;top:0;bottom:0;right:0;” 该样式能在尺寸上全等于目标,动态添加style标签,内容就是此次动画的颜色什么的描述 不同按钮颜色不同
- 收尾 清除各种冗余设置
// 1. 通过克隆来扩展组件 截取ref,拿到button原生dom对象
const { children } = this.props;
let ref: React.Ref<any> = this.containerRef;
if (supportRef(children)) {
ref = composeRef((children as any).ref, this.containerRef as any);
}
return cloneElement(children, { ref });
// 2. 获取颜色
const waveColor =
getComputedStyle(node).getPropertyValue('border-top-color') || // Firefox Compatible
getComputedStyle(node).getPropertyValue('border-color') ||
getComputedStyle(node).getPropertyValue('background-color');
// 3.onClick事件绑定
node.addEventListener('click', onClick, true);
// 4. 标记当前dom参与了波浪操作 便于后续取消操作
const { getPrefixCls } = this.context;
extraNode.className = `${getPrefixCls('')}-click-animating-node`;
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'true');
// 5. 设置相关的动态动画属性 用作视觉欺骗
styleForPseudo = updateCSS(
`
[${getPrefixCls('')}-click-animating-without-extra-node='true']::after, .${getPrefixCls(
'',
)}-click-animating-node {
--antd-wave-shadow-color: ${waveColor};
}`,
'antd-wave',
{ csp: this.csp, attachTo: nodeBody },
);
// 6. 插入傀儡标签
node.appendChild(extraNode);
// 经过两秒后动画完成
// 7. 清理现场
const attributeName = this.getAttributeName();
node.setAttribute(attributeName, 'false'); // edge has bug on `removeAttribute` #14466
if (styleForPseudo) {
styleForPseudo.innerHTML = '';
}
if (insertExtraNode && this.extraNode && node.contains(this.extraNode)) {
node.removeChild(this.extraNode);
}