React中如何向嵌套SkeletonTile子组件自动注入递增delay属性
可行实现方案
方案1:递归克隆子元素(无需修改现有SkeletonTile组件)
你之前修改props失败是因为直接修改了React元素的只读属性,使用React.cloneElement生成新的元素实例即可解决,该方法会自动合并原有props和新增props,不会丢失你自定义的width、height等属性。
import React, { Children, cloneElement, isValidElement } from 'react'; import SkeletonTile from './SkeletonTile'; type ControllerProps = { delayIncrease: number; children: React.ReactNode; }; const SkeletonController: React.FC<ControllerProps> = ({ delayIncrease, children }) => { // 维护delay计数器 let delayCount = 0; // 递归遍历子节点的函数 const processChildren = (nodes: React.ReactNode): React.ReactNode => { return Children.map(nodes, node => { // 非合法React元素直接返回 if (!isValidElement(node)) return node; // 匹配到SkeletonTile组件则注入delay if (node.type === SkeletonTile) { const currentDelay = delayCount * delayIncrease; delayCount++; // 优先使用用户手动传入的delay属性 return cloneElement(node, { delay: node.props.delay ?? currentDelay }); } // 其他元素递归处理子节点 if (node.props.children) { return cloneElement(node, { ...node.props, children: processChildren(node.props.children) }); } return node; }); }; return <>{processChildren(children)}</>; };
该方案优缺点:
- 优点:无需修改现有SkeletonTile组件,侵入性极低
- 缺点:嵌套层级极深时会有微小性能损耗,如果SkeletonTile被高阶组件包裹,需要通过静态
displayName属性做类型判断(比如判断node.type.displayName === 'SkeletonTile')
方案2:基于Context实现(性能更优,适配任意嵌套场景)
如果SkeletonTile可能嵌套在自定义组件内部,推荐用Context方案,不需要遍历克隆整个节点树,性能更优。
首先定义共享Context:
import { createContext, useContext } from 'react'; const SkeletonContext = createContext({ delayIncrease: 0.1, getNextDelay: () => 0, });
修改SkeletonTile组件,新增Context消费逻辑:
type Props = { delay?: number; width?: string; height?: string; }; const SkeletonTile: React.FC<Props> = ({ delay: customDelay, width, height }) => { const { getNextDelay } = useContext(SkeletonContext); // 优先使用用户手动传入的delay const delay = customDelay ?? getNextDelay(); return <div style={{ animationDelay: `${delay}s`, width, height }} className="skeleton-tile"></div>; };
实现SkeletonController:
type ControllerProps = { delayIncrease: number; children: React.ReactNode; }; const SkeletonController: React.FC<ControllerProps> = ({ delayIncrease, children }) => { let count = 0; const getNextDelay = () => { const current = count * delayIncrease; count++; return current; }; return ( <SkeletonContext.Provider value={{ delayIncrease, getNextDelay }}> {children} </SkeletonContext.Provider> ); };
该方案优缺点:
- 优点:性能更高,支持SkeletonTile嵌套在任意自定义组件内部使用,控制逻辑后续更易扩展
- 缺点:需要对现有SkeletonTile做少量修改
内容的提问来源于stack exchange,提问作者zekro
相关产品推荐
相关产品推荐

