React中使用组合模式替换子组件方法的最佳实践
问题背景
现有代码结构如下:
class DropZoneComp extends React.Component <any, any> { onDrop (file) { } render() { return ( <DropZone onDrop={this.onDrop.bind(this)}></DropZone>) } } export default function DropZoneParent(props) { const onDrop = () => { } return ( <DropZoneComp onDrop={onDrop}/> ) }
开发中遇到的核心问题:
- 类组件内部定义的
onDrop方法不属于props范畴,无法直接被父组件传入的逻辑替换 - 子组件的
onDrop方法本身需要调用setState、修改类实例上的非props属性(比如this.files、this.currentFile这类内部状态/实例变量) - 如果直接把自定义
onDrop逻辑完全放到父组件实现,需要在父组件同步维护和子组件同源的状态、实例变量,会产生大量逻辑冗余
最佳实现方案
采用子组件内部逻辑兜底 + 父组件自定义逻辑注入的扩展回调模式,完全不破坏现有子组件内部状态维护逻辑,不需要父组件冗余维护子组件内部状态,符合React组件封装规范:
- 子组件
DropZoneComp保留自身原有onDrop的全部内部逻辑,内部状态修改、实例变量更新全部留在子组件内部处理,不需要上浮到父组件 - 子组件接收可选的
onDropprops作为扩展入口(和原有props传参命名保持一致即可,不需要额外新增属性名) - 子组件自身的
onDrop方法执行完所有内部逻辑后,判断如果父组件传入了自定义回调,就把当前处理后的file对象、子组件内部维护的相关状态值作为参数传给父组件的自定义回调,父组件可以直接拿到这些值做扩展逻辑,不需要自己维护同源状态
改造后的代码示例:
class DropZoneComp extends React.Component <any, any> { // 子组件内部原有onDrop逻辑全部保留,不需要重构已有代码 onDrop (file) { // 原有内部逻辑:修改实例属性、更新state都放在这里 this.files = [...(this.files || []), file] this.currentFile = file this.setState({ currentFile: file, fileList: this.files }, () => { // 内部逻辑执行完成后,调用父组件传入的自定义回调,透传必要上下文 if (typeof this.props.onDrop === 'function') { // 传参可按需调整:file对象、子组件内部状态、子组件实例都可以透传 this.props.onDrop(file, { files: this.files, currentFile: this.currentFile }) } }) } render() { return ( <DropZone onDrop={this.onDrop.bind(this)}></DropZone>) } } export default function DropZoneParent(props) { // 父组件只需要实现自身扩展逻辑,不需要维护子组件内部的files、currentFile状态 const onDrop = (file, childContext) => { // 直接拿到子组件已经维护好的状态做业务处理即可 console.log('待上传文件', file, '子组件维护的文件列表', childContext.files) // 上传接口请求、额外业务校验等逻辑都可以在这里实现 } return ( <DropZoneComp onDrop={onDrop}/> ) }
方案优势
- 零侵入:完全兼容子组件原有运行逻辑,不需要重构已有代码
- 无冗余:子组件内部状态、实例变量的维护逻辑全部收敛在子组件内部,父组件不需要重复维护同源状态
- 灵活性高:父组件不传自定义
onDrop时,子组件完全按照原有逻辑运行;传入时默认在子组件内部逻辑执行完成后追加自定义业务逻辑。如果需要支持父组件逻辑前置、或者完全覆盖子组件默认逻辑,只需要在子组件的onDrop方法里调整判断顺序即可,比如约定父组件回调返回false时阻断子组件后续默认逻辑,不需要改动整体结构 - 低耦合:子组件只对外暴露必要的扩展入口,内部状态实现细节不需要父组件感知,符合组件封装原则
内容的提问来源于stack exchange,提问作者user11092881
相关产品推荐
相关产品推荐

