如何复用React createPortal属性,提取多模态框公共逻辑?
React多模态框冗余代码优化方案
问题背景
我们的React应用中有多个功能相似的模态框,已经将createPortal的切换逻辑抽入PortalView组件,但仍需重复传递handleCancel和handleSave作为子组件的默认属性,随着模态框数量增加,冗余代码越来越多。现有抽离后的写法仍存在重复传参的问题,纠结是否使用render props、React.Children或React.cloneElement,又担心这些方法在现代React中不被推荐。
现有抽离后的代码(仍有冗余):
<PortalView viewMode={viewMode} views={{ modal1: <Modal1 onCancel={handleCancel} onSave={handleSave} additionalDifferentProp="foo"/>, modal2: <Modal2 onCancel={handleCancel} onSave={handleSave} additionalDifferentProp2="bar"/>, }} />
优化思路:自动注入默认Props
核心思路是让PortalView组件承担起默认onCancel和onSave的注入工作,避免每个模态框手动传参。以下提供两种可行方案,均不存在“过度复杂”的问题,且在合适场景下完全符合React的设计理念。
方案1:使用React.cloneElement(简洁型)
通过React.cloneElement克隆传入的模态框组件,自动合并默认的onCancel和onSave,同时保留组件自身传入的props(若有自定义逻辑可覆盖默认值)。
改造后的PortalView组件:
import { createPortal } from 'react-dom'; export default function PortalView({ viewMode, views, onCancel, onSave }) { const currentView = views[viewMode]; if (!currentView) return null; // 克隆组件并注入默认props,组件自身的props优先级更高 const enhancedView = React.cloneElement(currentView, { onCancel: currentView.props.onCancel || onCancel, onSave: currentView.props.onSave || onSave, }); return createPortal(enhancedView, document.body); }
App中使用方式:
function App() { const [viewMode, setViewMode] = useState("default") const handleCancel = () => setViewMode("default"); const handleSave = () => { setViewMode("default"); reloadData(); }; return ( <> <PortalView viewMode={viewMode} onCancel={handleCancel} onSave={handleSave} views={{ modal1: <Modal1 additionalDifferentProp="foo"/>, modal2: <Modal2 additionalDifferentProp2="bar"/>, // 若需自定义某个模态框的回调,直接传入即可覆盖默认值 modal3: <Modal3 customProp="baz" onSave={() => { /* 自定义保存逻辑 */ }}/>, }} /> </> ) }
方案2:使用Render Props(灵活型)
如果部分模态框需要基于默认回调做额外处理,可采用render props模式,让每个视图项是一个接收默认回调的函数,返回模态框组件。
改造后的PortalView组件:
import { createPortal } from 'react-dom'; export default function PortalView({ viewMode, views, onCancel, onSave }) { const renderView = views[viewMode]; if (!renderView) return null; const viewElement = renderView({ onCancel, onSave }); return createPortal(viewElement, document.body); }
App中使用方式:
function App() { const [viewMode, setViewMode] = useState("default") const handleCancel = () => setViewMode("default"); const handleSave = () => { setViewMode("default"); reloadData(); }; return ( <> <PortalView viewMode={viewMode} onCancel={handleCancel} onSave={handleSave} views={{ modal1: ({ onCancel, onSave }) => ( <Modal1 onCancel={onCancel} onSave={onSave} additionalDifferentProp="foo"/> ), modal2: ({ onCancel, onSave }) => ( <Modal2 onCancel={onCancel} onSave={onSave} additionalDifferentProp2="bar"/> ), // 可在函数内添加自定义逻辑 modal3: ({ onCancel, onSave }) => { const customSave = () => { console.log("额外逻辑"); onSave(); }; return <Modal3 onCancel={onCancel} onSave={customSave} customProp="baz"/>; }, }} /> </> ) }
方案选择建议
- 若只是需要简单注入默认回调,优先选方案1,写法更简洁,冗余最少。
- 若部分模态框需要对默认回调做扩展处理,选方案2,灵活性更高。
无需纠结这些方法是否“不被现代React推荐”——它们都是React官方提供的API,只要在合适的场景下使用(比如这里的组件封装、props注入),完全符合React的设计原则,不存在过度复杂的问题。
内容的提问来源于stack exchange,提问作者jansepke
相关产品推荐
相关产品推荐

