在 React 18 中,通过 ref.current?.open('create') 实现组件封装是一种命令式的控制方式。这种方式的核心思想是将组件的内部逻辑(如打开、关闭)封装在组件内部,并通过 ref 暴露给父组件调用。以下是详细的实现步骤:
- 封装组件
使用 forwardRef 和 useImperativeHandle 将组件的内部方法(如 open)暴露给父组件。
示例代码:
javascript
import React, { useRef, useImperativeHandle, forwardRef, useState } from 'react';
import './MyComponent.css'; // 组件样式
const MyComponent = forwardRef((props, ref) => {
const [isOpen, setIsOpen] = useState(false);
const [mode, setMode] = useState(''); // 用于区分模式,如 'create'
// 暴露 open 方法给父组件
useImperativeHandle(ref, () => ({
open: (mode) => {
setMode(mode);
setIsOpen(true);
},
close: () => {
setIsOpen(false);
},
}));
if (!isOpen) return null;
return (
<div className="component-overlay">
<div className="component-content">
<button className="component-close" onClick={() => setIsOpen(false)}>
×
</button>
{mode === 'create' && <CreateForm />}
{mode === 'edit' && <EditForm />}
</div>
</div>
);
});
// 示例表单组件
const CreateForm = () => (
<div>
<h2>创建模式</h2>
<form>
<input type="text" placeholder="输入内容" />
<button type="submit">提交</button>
</form>
</div>
);
const EditForm = () => (
<div>
<h2>编辑模式</h2>
<form>
<input type="text" placeholder="编辑内容" />
<button type="submit">保存</button>
</form>
</div>
);
export default MyComponent;
- 在父组件中使用
父组件通过 useRef 获取组件的引用,并调用 ref.current?.open('create') 打开组件。
示例代码:
javascript
import React, { useRef } from 'react';
import MyComponent from './MyComponent';
function App() {
const componentRef = useRef(null);
const handleOpenCreate = () => {
componentRef.current?.open('create'); // 打开创建模式
};
const handleOpenEdit = () => {
componentRef.current?.open('edit'); // 打开编辑模式
};
return (
<div>
<button onClick={handleOpenCreate}>打开创建模式</button>
<button onClick={handleOpenEdit}>打开编辑模式</button>
<MyComponent ref={componentRef} />
</div>
);
}
export default App;
- 样式文件 MyComponent.css
确保组件的样式正确:
css
.component-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
}
.component-content {
background-color: white;
padding: 20px;
border-radius: 8px;
position: relative;
max-width: 500px;
width: 100%;
}
.component-close {
position: absolute;
top: 10px;
right: 10px;
background: none;
border: none;
font-size: 20px;
cursor: pointer;
}
- 实现原理
forwardRef:将 ref 传递给子组件。
useImperativeHandle:在子组件中暴露方法(如 open 和 close)给父组件。
命令式调用:父组件通过 ref.current?.open('create') 控制组件的显示。
- 支持更多功能
动态内容:可以通过 open 方法传递更多参数,动态渲染内容。
关闭回调:在 close 方法中添加回调函数,处理关闭后的逻辑。
动画效果:结合 react-transition-group 实现动画。
示例:动态内容
javascript
useImperativeHandle(ref, () => ({
open: (mode, data) => {
setMode(mode);
setData(data); // 设置动态数据
setIsOpen(true);
},
}));
- 总结
通过 useRef 和 useImperativeHandle,可以实现命令式的组件控制,使父组件能够直接调用组件的 open 和 close 方法。这种方式非常适合需要动态控制组件显示的场景,代码结构清晰且易于维护。