React中的设计模式实践
React作为现代前端开发的主流框架,其组件化思想与设计模式天然契合。合理运用设计模式能提升代码的可维护性、复用性和扩展性,尤其在复杂应用场景下效果显著。以下是React中常见设计模式的实践解析。
组合模式(Compound Components)
组合模式通过组件嵌套实现功能解耦,典型例子是<select>
和<option>
的关系。在React中,可以通过React.Context
和React.cloneElement
实现:
const Tabs = ({ children }) => {
const [activeIndex, setActiveIndex] = useState(0);
return (
<TabContext.Provider value={{ activeIndex, setActiveIndex }}>
<div className="tabs">{children}</div>
</TabContext.Provider>
);
};
const TabList = ({ children }) => {
const { activeIndex, setActiveIndex } = useContext(TabContext);
return React.Children.map(children, (child, index) =>
React.cloneElement(child, {
isActive: index === activeIndex,
onClick: () => setActiveIndex(index)
})
);
};
// 使用示例
<Tabs>
<TabList>
<Tab>首页</Tab>
<Tab>产品</Tab>
</TabList>
<TabPanels>
<TabPanel>首页内容</TabPanel>
<TabPanel>产品列表</TabPanel>
</TabPanels>
</Tabs>
这种模式让父组件管理状态,子组件通过上下文获取状态,保持UI与逻辑分离。
渲染属性模式(Render Props)
通过函数作为子组件(children prop)传递动态内容,实现逻辑复用:
const MouseTracker = ({ children }) => {
const [position, setPosition] = useState({ x: 0, y: 0 });
const handleMouseMove = (e) => setPosition({ x: e.clientX, y: e.clientY });
return (
<div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
{children(position)}
</div>
);
};
// 使用示例
<MouseTracker>
({ x, y }) => (
<h1>
鼠标位置: {x}, {y}
</h1>
)
</MouseTracker>
更灵活的变体是使用任意prop名称:
<DataProvider render={(data) => <Child data={data} />} />
高阶组件(HOC)
通过函数包装组件实现横切关注点:
const withLoading = (WrappedComponent) => {
return (props) => {
const [loading, setLoading] = useState(true);
useEffect(() => {
const timer = setTimeout(() => setLoading(false), 1000);
return () => clearTimeout(timer);
}, []);
return loading ? <Spinner /> : <WrappedComponent {...props} />;
};
};
// 使用示例
const UserProfile = ({ user }) => <div>{user.name}</div>;
const UserProfileWithLoading = withLoading(UserProfile);
注意HOC可能导致ref丢失,需要使用React.forwardRef
解决。
状态提升与单一数据源
在表单组件中常见状态提升模式:
const FormContainer = () => {
const [formData, setFormData] = useState({
username: '',
password: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
return (
<form>
<Input
name="username"
value={formData.username}
onChange={handleChange}
/>
<Input
name="password"
type="password"
value={formData.password}
onChange={handleChange}
/>
</form>
);
};
提供者模式(Provider Pattern)
通过Context API实现全局状态管理:
const ThemeContext = React.createContext('light');
const App = () => {
const [theme, setTheme] = useState('dark');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Toolbar />
</ThemeContext.Provider>
);
};
const ThemedButton = () => {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
style={{ background: theme === 'dark' ? '#333' : '#EEE' }}
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
>
切换主题
</button>
);
};
容器/展示组件模式
分离逻辑与UI的经典模式:
// 容器组件
const UserListContainer = () => {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetchUsers().then(data => {
setUsers(data);
setLoading(false);
});
}, []);
return <UserList users={users} loading={loading} />;
};
// 展示组件
const UserList = ({ users, loading }) => (
loading ? <Spinner /> : (
<ul>
{users.map(user => (
<li key={user.id}>{user.name}</li>
))}
</ul>
)
);
自定义Hook模式
将组件逻辑提取为可复用Hook:
const useLocalStorage = (key, initialValue) => {
const [value, setValue] = useState(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : initialValue;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
};
// 使用示例
const Counter = () => {
const [count, setCount] = useLocalStorage('counter', 0);
return (
<button onClick={() => setCount(c => c + 1)}>
点击次数: {count}
</button>
);
};
策略模式在表单验证中的应用
动态切换验证规则:
const validationStrategies = {
email: (value) => /^\S+@\S+$/.test(value),
password: (value) => value.length >= 8
};
const useValidation = (strategyName) => {
const validate = validationStrategies[strategyName];
const [error, setError] = useState('');
const check = (value) => {
const isValid = validate(value);
setError(isValid ? '' : `Invalid ${strategyName}`);
return isValid;
};
return [error, check];
};
// 使用示例
const EmailInput = () => {
const [email, setEmail] = useState('');
const [error, validateEmail] = useValidation('email');
return (
<>
<input
type="text"
value={email}
onChange={(e) => setEmail(e.target.value)}
onBlur={() => validateEmail(email)}
/>
{error && <div className="error">{error}</div>}
</>
);
};
观察者模式与事件总线
实现组件间松耦合通信:
const EventBus = {
events: {},
subscribe(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
},
publish(event, data) {
(this.events[event] || []).forEach(cb => cb(data));
}
};
// 发布者组件
const Publisher = () => (
<button onClick={() => EventBus.publish('alert', '按钮被点击')}>
触发事件
</button>
);
// 订阅者组件
const Subscriber = () => {
const [message, setMessage] = useState('');
useEffect(() => {
EventBus.subscribe('alert', setMessage);
return () => EventBus.unsubscribe('alert', setMessage);
}, []);
return <div>{message}</div>;
};
工厂模式创建动态组件
根据配置生成不同组件:
const componentMap = {
text: ({ value }) => <span>{value}</span>,
image: ({ src }) => <img src={src} alt="" />,
button: ({ label, onClick }) => (
<button onClick={onClick}>{label}</button>
)
};
const DynamicComponent = ({ type, ...props }) => {
const Component = componentMap[type] || componentMap.text;
return <Component {...props} />;
};
// 使用示例
const configs = [
{ type: 'text', value: '静态文本' },
{ type: 'button', label: '提交', onClick: () => alert('点击') }
];
const ComponentFactory = () => (
<div>
{configs.map((config, i) => (
<DynamicComponent key={i} {...config} />
))}
</div>
);
备忘录模式与状态快照
实现撤销/重做功能:
const useHistory = (initialState) => {
const [history, setHistory] = useState([initialState]);
const [index, setIndex] = useState(0);
const current = history[index];
const push = (newState) => {
const newHistory = history.slice(0, index + 1);
setHistory([...newHistory, newState]);
setIndex(newHistory.length);
};
const undo = () => index > 0 && setIndex(i => i - 1);
const redo = () => index < history.length - 1 && setIndex(i => i + 1);
return [current, push, { undo, redo, canUndo: index > 0, canRedo: index < history.length - 1 }];
};
// 使用示例
const SketchPad = () => {
const [paths, setPaths, { undo, redo }] = useHistory([]);
const handleDraw = (newPath) => {
setPaths([...paths, newPath]);
};
return (
<div>
<Canvas onDraw={handleDraw} paths={paths} />
<button onClick={undo} disabled={!canUndo}>撤销</button>
<button onClick={redo} disabled={!canRedo}>重做</button>
</div>
);
};
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
下一篇:Vue的响应式系统与设计模式