从零开始打造可复用的React组件并实现深度定制化
我的写法,亲测靠谱
先说说我常用的组件定制写法吧。最近一个项目里用到了这样的方式:
import BaseButton from './BaseButton';
const CustomButton = ({ color, size, children, ...rest }) => {
const styles = {
small: { padding: '5px 10px', fontSize: '12px' },
medium: { padding: '8px 16px', fontSize: '14px' },
large: { padding: '12px 24px', fontSize: '16px' }
};
return (
<BaseButton
style={{
backgroundColor: color,
...styles[size]
}}
{...rest}
>
{children}
</BaseButton>
);
};
这种方式有几个好处:首先是把样式和逻辑分离得很清楚,每个size都对应一套固定样式,维护起来特别方便。其次是通过解构传参,让组件的扩展性很强。
我一般还会在BaseButton里做一些基础封装,比如:
const BaseButton = ({ onClick, disabled, className, children, ...props }) => {
return (
<button
className={base-btn ${className || ''}}
onClick={disabled ? undefined : onClick}
disabled={disabled}
{...props}
>
{children}
</button>
);
};
这里加了个基础的disabled处理,防止误触。别看这代码简单,能省不少事。
这几种错误写法,别再踩坑了
说真的,踩过的坑太多了。最常见的就是直接在组件里硬编码样式:
const BadButton = () => (
<button style={{ backgroundColor: '#ff0000', padding: '10px', border: 'none' }}>
Click me
</button>
);
这种写法看着简单,但是一旦要改样式就得挨个找组件修改,折腾死人。
还有种常见的错误是滥用props:
const AnotherBadButton = (props) => (
<button
style={{
backgroundColor: props.bgColor,
color: props.textColor,
padding: props.padding,
margin: props.margin,
// 各种乱传...
}}
>
{props.children}
</button>
);
这样写看似灵活,实际上太随意了,很容易导致组件行为不可控,建议避开这种写法。
实际项目中的坑
说个最近遇到的坑。有个需求是要根据不同权限显示不同样式的按钮,一开始我是这么写的:
const PermissionButton = ({ userRole, ...props }) => {
if (userRole === 'admin') {
return <AdminButton {...props} />;
} else if (userRole === 'editor') {
return <EditorButton {...props} />;
}
return <BasicButton {...props} />;
};
看起来没毛病对吧?结果上线后发现,权限变更时页面不会实时更新。折腾了半天才发现,应该用React的状态管理来处理:
const [role, setRole] = useState(userRole);
useEffect(() => {
const checkPermission = async () => {
const response = await fetch('https://jztheme.com/api/permission');
const data = await response.json();
setRole(data.role);
};
checkPermission();
}, []);
这才解决了问题。所以状态相关的逻辑一定要慎重处理。
性能优化的小技巧
说到性能,有件事得提醒下。之前为了给按钮加loading效果,直接在组件里用了setTimeout:
const LoadingButton = ({ isLoading }) => {`>
const [text, setText] = useState('Submit');useEffect(() => {
if (isLoading) {
setText('Loading...');
setTimeout(() => setText('Submit'), 3000); // 这里有问题
}
}, [isLoading]);return <button>{text}</button>;
};
`>
<p>结果发现切换太快会导致状态混乱。后来改成这样:</p></code></pre>javascript
const LoadingButton = ({ isLoading }) => {
const timerRef = useRef();useEffect(() => {
if (isLoading) {
timerRef.current = setTimeout(() => {
// 处理逻辑
}, 3000);
}return () => clearTimeout(timerRef.current);
}, [isLoading]);return <button>{isLoading ? 'Loading...' : 'Submit'}</button>;
};
用useRef存定时器ID,确保每次都能正确清除,这个小改动让组件稳定多了。
结尾唠叨几句
以上是我总结的最佳实践,当然方案不是完美的,但确实是实战中摸索出来的比较靠谱的写法。组件定制这块水挺深的,不同的项目可能需要不同的处理方式。
我个人觉得,写组件最重要的是:明确职责边界。该组件做的事就做好,不该管的别瞎掺和。像样式这种东西,能抽出去就抽出去,别全堆在组件里。
有更好的方案欢迎评论区交流,或者你有遇到什么奇葩需求也可以分享下,大家一起讨论。

暂无评论