如何写出优雅的React代码Clean Code vs. Dirty Code

如何写出优雅的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/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值