不使用React顶层API 如何为复合子组件动态添加props
问题根因说明
你之前的方案触发Panel重挂载的核心问题并非使用了React.Children和cloneElement,而是每次渲染都通过getGuid()给Panel生成了新的随机key,React识别到key变更后会直接卸载旧组件、挂载新组件,才导致不符合预期的体验问题。
可行实现方案
方案1:Context注入(完全不需要操作子组件props,推荐)
该方案不需要使用任何React顶层API修改子组件属性,不会触发任何不必要的重挂载,是耦合度最低的实现方式。
- 首先创建公共Context:
import { createContext, useContext } from 'react'; const PanelGroupContext = createContext<{ baseZIndex: number; getPanelZIndex: (panelId: string, position: string, isNested: boolean) => number; }>({ baseZIndex: 999, getPanelZIndex: () => 999, }); export const usePanelGroupContext = () => useContext(PanelGroupContext);
- 实现PanelGroup组件:
const PanelGroup = ({ children }: PanelGroupProp): JSX.Element => { const [baseZIndex, setBaseZIndex] = useState<number>(999); // 初始加载时计算页面最高z-index useEffect(() => { const highestIndex = getHighestZIndex(); setBaseZIndex(highestIndex); }, []); // 按你的规则实现z-index计算逻辑:结合定位、是否嵌套计算增量 const getPanelZIndex = (panelId: string, position: string, isNested: boolean) => { // 示例权重规则,可按需调整 const positionWeight = { [PanelPosition.TOP]: 10, [PanelPosition.BOTTOM]: 9, [PanelPosition.LEFT]: 8, [PanelPosition.RIGHT]: 7, }[position] || 0; const nestedWeight = isNested ? 20 : 0; return baseZIndex + positionWeight + nestedWeight; }; return ( <PanelGroupContext.Provider value={{ baseZIndex, getPanelZIndex }}> <PanelOverlay visibility={false} /> {/* 直接透传children,不做任何修改,完全不会触发重挂载 */} {children} </PanelGroupContext.Provider> ); };
- Panel组件内部消费Context获取z-index:
const Panel = ({ id, position, isNested = false, ...rest }: PanelProp) => { const { getPanelZIndex } = usePanelGroupContext(); // 自行计算当前Panel对应的z-index const zIndex = getPanelZIndex(id, position, isNested); // 后续将z-index绑定到Panel根节点的style上即可 return <div style={{ zIndex }} {...rest} />; };
方案2:修正cloneElement的key属性(不需要修改Panel组件代码)
如果你不想调整现有Panel组件的逻辑,只要修改原有cloneElement的key生成规则,即可解决重挂载问题:
const PanelGroup = ({ children }: PanelGroupProp): JSX.Element => { const [highestZIndex, setHighestZIndex] = React.useState(null); React.useEffect(() => { const highestIndex = getHighestZIndex(); setHighestZIndex(highestIndex); }, []); const renderGroupPanelChildren = (children): React.ReactElement => { let incrementor = highestZIndex + 1; return ( <React.Fragment> {React.Children.map(children || null, (child) => { if (!React.isValidElement(child) || child.type !== Panel) return child; const newProps = {...child.props, zIndex: incrementor }; incrementor++; // 用Panel自身的id作为key,不要生成随机key,React就不会触发重挂载 return React.cloneElement(child, newProps); })} </React.Fragment> ); }; return ( <> <PanelOverlay visibility={false} /> {renderGroupPanelChildren(children)} </> ); };
内容的提问来源于stack exchange,提问作者Aaron Balthaser
相关产品推荐
相关产品推荐

