如何写出优雅的React代码Clean Code vs. Dirty Code
不要写重复的代码
// Dirty
const MyComponent = () => (
<div>
<OtherComponent type="a" className="colorful" foo={123} bar={456} />
<OtherComponent type="b" className="colorful" foo={123} bar={456} />
</div>
);
去掉重复部分
// Clean
const MyOtherComponent = ({ type }) => (
<OtherComponent type={type} className="colorful" foo={123} bar={456} />
);
const MyComponent = () => (
<div>
<MyOtherComponent type="a" />
<MyOtherComponent type="b" />
</div>
);
这样看起来,虽然增加了代码量,但是长远考虑,这样提高了代码的可维护性。
自我注释
好的代码是可以自我注释的。
写代码一个好的习惯就是写注释,注释里面写清楚代码的逻辑,但是有的时候,可能对代码逻辑进行了修改,忘记了的改注释。那下一个看代码的人,就会感到很困惑,注释与代码逻辑不一致。
注释可以来解释一个复杂的代码逻辑,但是对于一个简单的逻辑上有多余的注释,或者复杂的逻辑上有个不正确的注释,都会使人感到混乱。
// Dirty
const fetchUser = (id) => (
fetch(buildUri`/users/${id}`) // Get User DTO record from REST API
.then(convertFormat) // Convert to snakeCase
.then(validateUser) // Make sure the the user is valid
);
我们来重命名一个代码功能,消除代码注释。
// Clean
const fetchUser = (id) => (
fetch(buildUri`/users/${id}`)
.then(snakeToCamelCase)
.then(validateUser)
);
现在我们重命名了功能,可以从功能上直观的看出来它的含义。
命名
命名包括变量名、函数名以及文件名。
规则
1 布尔变量或返回布尔值的函数应以“ is”,“ has”或“ should”开头
// Dirty
const done = current >= goal;
// Clean
const isComplete = current >= goal;
2 函数应该根据它做什么而不是怎么做来命名。
就是不要在函数名称中写上它实现的方式信息。为什么呢,因为你的实现方式可能会变。
// Dirty
const loadConfigFromServer = () => {
...
};
// Clean
const loadConfig = () => {
...
};
一些建议在构建中
使用小的功能,每个功能都有一个职责。这称为单一责任原则。确保每个功能都能完成一项工作。这可能意味着将复杂的组件分解为许多较小的组件。这也将导致更好的可测试性。
随时注意泄漏的抽象。换句话说,不要将内部要求强加给代码使用者。
遵守严格的lint规则。这将帮助您编写干净,一致的代码。
一些例子
1 两个组件,一个带标题,一个不带标题。
// Dirty
import Title from './Title';
export const Thingie = ({ description }) => (
<div class="thingie">
<div class="description-wrapper">
<Description value={description} />
</div>
</div>
);
export const ThingieWithTitle = ({ title, description }) => (
<div>
<Title value={title} />
<div class="description-wrapper">
<Description value={description} />
</div>
</div>
);
// Clean
import Title from './Title';
export const Thingie = ({ description, children }) => (
<div class="thingie">
{children}
<div class="description-wrapper">
<Description value={description} />
</div>
</div>
);
export const ThingieWithTitle = ({ title, ...others }) => (
<Thingie {...others}>
<Title value={title} />
</Thingie>
);
2 默认值
// Dirty
const Icon = ({ className, onClick }) => {
const additionalClasses = className || 'icon-large';
return (
<span
className={`icon-hover ${additionalClasses}`}
onClick={onClick}>
</span>
);
};
用到了参数默认值,和ES6中箭头函数单句返回。
// Clean
const Icon = ({ className = 'icon-large', onClick }) => (
<span className={`icon-hover ${className}`} onClick={onClick} />
);
更好的一种方式,默认值放在最外面。
// Cleaner
const Icon = ({ className, onClick }) => (
<span className={`icon-hover ${className}`} onClick={onClick} />
);
Icon.defaultProps = {
className: 'icon-large',
};
但是,让React设置prop默认值的好处是,它可以产生更有效的代码,在基于Class的生命周期组件中默认prop,以及允许根据propTypes检查默认值。但是还有一个优势:它可以将默认逻辑从组件本身的默认逻辑中整理出来。
例如,您可以执行以下操作,将所有默认道具存储在一个地方。我不建议您这样做;我只是说您有这样做的灵活性。
import defaultProps from './defaultProps';
...
Icon.defaultProps = defaultProps.Icon;
3 状态与呈现分开
将状态数据加载逻辑与呈现(或表示)逻辑混合使用会导致组件复杂。而是编写一个有状态的容器组件,其唯一职责是加载数据。然后编写另一个组件,其唯一职责是显示数据。这称为容器模式。
// Dirty
class User extends Component {
state = { loading: true };
render() {
const { loading, user } = this.state;
return loading
? <div>Loading...</div>
: <div>
<div>
First name: {user.firstName}
</div>
<div>
First name: {user.lastName}
</div>
...
</div>;
}
componentDidMount() {
fetchUser(this.props.id)
.then((user) => { this.setState({ loading: false, user })})
}
}
// Clean
import RenderUser from './RenderUser';
class User extends Component {
state = { loading: true };
render() {
const { loading, user } = this.state;
return loading ? <Loading /> : <RenderUser user={user} />;
}
componentDidMount() {
fetchUser(this.props.id)
.then(user => { this.setState({ loading: false, user })})
}
}
使用无状态功能组件
// Dirty
class TableRowWrapper extends Component {
render() {
return (
<tr>
{this.props.children}
</tr>
);
}
}
干净版本清除了许多脏版本的屏幕混乱。通过优化React的核心,可以使用更少的内存,因为不会创建任何实例。
// Clean
const TableRowWrapper = ({ children }) => (
<tr>
{children}
</tr>
);
ES6 中的新特性
// Dirty
const MyComponent = (props) => {
const others = Object.assign({}, props);
delete others.className;
return (
<div className={props.className}>
{React.createElement(MyOtherComponent, others)}
</div>
);
};
// Clean
const MyComponent = ({ className, ...others }) => (
<div className={className}>
<MyOtherComponent {...others} />
</div>
);
// Dirty
componentWillReceiveProps(newProps) {
this.setState({
active: newProps.active
});
}
// Clean
componentWillReceiveProps({ active }) {
this.setState({ active });
}
// Dirty
const splitLocale = locale.split('-');
const language = splitLocale[0];
const country = splitLocale[1];
// Clean
const [language, country] = locale.split('-');
引用:
https://americanexpress.io/clean-code-dirty-code/