Render Prop模式下如何实现子组件回调并向渲染props传参?
解决Render Prop模式下子组件操作传递问题
核心思路
要捕获ColumnAction内部的宽度调整、列重排等操作并通知父组件,关键是让ColumnAction在内部操作触发状态/属性变更时,把变更信息通过Render Prop的参数暴露出来,父组件在渲染回调里接收这些参数后,就能调用主组件的对应操作。
步骤1:改造ColumnAction组件
在ColumnAction内部,处理mouseDown/mouseUp等事件导致列属性变化时,把需要传递的信息(比如列标识、新宽度、操作类型)合并到传给Render Prop的参数中。示例如下:
// ColumnAction.jsx import { useState, useRef, useEffect } from 'react'; const ColumnAction = ({ children, columnKey }) => { const [columnWidth, setColumnWidth] = useState(180); // 初始宽度 const ref = useRef(null); const ref2 = useRef(null); let startX = 0; let startWidth = 0; // 处理列宽度调整逻辑 const handleMouseDown = (e) => { startX = e.clientX; startWidth = ref2.current.offsetWidth; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }; const handleMouseMove = (e) => { const newWidth = startWidth + (e.clientX - startX); setColumnWidth(newWidth); }; const handleMouseUp = () => { document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; useEffect(() => { if (ref.current) { ref.current.addEventListener('mousedown', handleMouseDown); } return () => { if (ref.current) { ref.current.removeEventListener('mousedown', handleMouseDown); } }; }, []); // 把ref、ref2和变更信息一起传给render函数 return children({ ref, ref2, columnWidth, columnKey, operationType: 'resize' // 标识操作类型,方便父组件区分 }); }; export default ColumnAction;
步骤2:在父组件中接收参数并触发主组件操作
修改你现有的TableHead代码,在Render Prop的回调里接收新增的参数,通过useEffect监听属性变化,触发主组件传入的回调方法:
const TableHead = props.selectedAttributes.map(attribute => { const attributeDetail = props.attributes.find(attr => attr.attribute === attribute); return ( <ColumnAction key={attribute} columnKey={attribute}> {({ ref, ref2, columnWidth, columnKey }) => { // 监听列宽度变化,通知主组件 useEffect(() => { props.onColumnChange({ key: columnKey, width: columnWidth, type: 'resize' }); }, [columnWidth, columnKey, props.onColumnChange]); return ( <th className='UniveralGridTableHead draggable' data-attrb={attribute} data-menu={props.menu} ref={ref2} style={{ width: `${columnWidth}px` }} // 应用调整后的宽度 > {attributeDetail.label} <i className='sortDesc' style={{ display: attributeDetail.sortable ? 'inline-block' : 'none', borderTop: props.orderBy === attribute && props.orderDirection === 'DESC' ? '5px solid #EEEEEE' : '5px solid #888888' }} onClick={() => props.conditionCallback('sortDesc', attribute)} ></i> <i className='sortAsc' style={{ display: attributeDetail.sortable ? 'inline-block' : 'none', borderBottom: props.orderBy === attribute && props.orderDirection === 'ASC' ? '5px solid #EEEEEE' : '5px solid #888888' }} onClick={() => props.conditionCallback('sortAsc', attribute)} ></i> <div className='ColumnResizer' ref={ref}></div> </th> ); }} </ColumnAction> ); });
步骤3:主组件中处理回调
在主组件里定义onColumnChange方法,接收子组件传来的变更信息,触发后续操作:
const MainTable = () => { const handleColumnChange = (changeInfo) => { // 根据变更信息执行对应逻辑,比如更新列配置缓存、触发表格数据重新渲染等 console.log(`列${changeInfo.key}宽度变为${changeInfo.width}px`); // 这里可以调用其他需要触发的业务操作 }; return ( <table> <thead> {TableHead({ selectedAttributes: ['id', 'name', 'age'], attributes: [...], conditionCallback: (...args) => {}, onColumnChange: handleColumnChange // 传入回调方法 })} </thead> <tbody>...</tbody> </table> ); };
额外说明
如果ColumnAction里的操作(比如列重排)不是状态驱动的,也可以直接在操作完成时调用props传入的回调,比如给ColumnAction加一个onReorder属性,内部重排完成后直接调用props.onReorder(columnKey, newPosition),这种方式更直接。
内容的提问来源于stack exchange,提问作者Maker
相关产品推荐
相关产品推荐

