React 最佳实践

栏目: IOS · Android · 发布时间: 6年前

内容简介:欢迎关注我的公众号在日常开发和 Code Review 的时候,常常会发现一些共性的问题,也有很多值得提倡的做法。本文针对 React 技术栈,总结了一些最佳实践,对编写高质量的代码有一定的参考作用。

欢迎关注我的公众号 睿Talk ,获取我最新的文章:

React 最佳实践

一、前言

在日常开发和 Code Review 的时候,常常会发现一些共性的问题,也有很多值得提倡的做法。本文针对 React 技术栈,总结了一些最佳实践,对编写高质量的代码有一定的参考作用。

二、最佳实践 & 说明

  • 多用 Function Component

如果组件是纯展示型的,不需要维护 state 和生命周期,则优先使用 Function Component。它有如下好处:

  1. 代码更简洁,一看就知道是纯展示型的,没有复杂的业务逻辑
  2. 更好的复用性。只要传入相同结构的 props,就能展示相同的界面,不需要考虑副作用。
  3. 更小的打包体积,更高的执行效率
  • 多用 PureComponent

如果组件需要维护 state 或使用生命周期方法,则优先使用 PureComponent,而不是 Component。Component 的默认行为是不论 state 和 props 是否有变化,都触发 render。而 PureComponent 会先对 state 和 props 进行浅比较,不同的时候才会 render。请看下面的例子:

class Child extends React.Component {
  render() {
    console.log('render Child');
    return (
      <div>
        {this.props.obj.num}
      </div>
    );
  }
}

class App extends React.Component {
  state = {
    obj: { num: 1 }
  };

  onClick = () => {
    const {obj} = this.state;
    this.setState({obj});
  }

  render() {
    console.log('render Parent');
    return (
      <div className="App" >
        <button onClick={this.onClick}>
          点我
        </button>
        <Child obj={this.state.obj}/>
      </div>
    );
  }
}

点击按钮后,Parent 和 Child 的 render 都会触发。如果将 Child 改为 PureComponent,则 Child 的 render 不会触发,因为 props 还是同一个对象。如果将 Parent 也改为 PureComponent,则 Parent 的 render 也不会触发了,因为 state 还是同一个对象。

  • 定义组件时,定义 PropTypes 和 defaultProps

例子如下:

class CategorySelector extends PureComponent {
    ...
}

CategorySelector.propTypes = {
    type: PropTypes.string,
    catList: PropTypes.array.isRequired,
    default: PropTypes.bool,
};

CategorySelector.defaultProps = {
    default: false,
    type: undefined,
};
  • 避免在 render 里面动态创建对象 / 方法,否则会导致子组件每次都 render
render() {
    const obj = {num: 1}
    
    return(
        <Child obj={obj} onClick={()=>{...}} />
    );
}

在上面代码中,即使 Child 是 PureComponent,由于 obj 和 onClick 每次 render 都是新的对象,Child 也会跟着 render。

  • 避免在 JSX 中写复杂的三元表达式,应通过封装函数或组件实现
render() {
    const a = 8;
    
    return (
        <div>
            {
                a > 0 ? a < 9 ? ... : ... : ...
            }
        </div>
    );    
}

避免写像上面这种嵌套的三元表达式。可以写成下面的形式:

f() {
    ...
}

render() {
    const a = 8;
    
    return (
        <div>
            {
                this.f()
            }
        </div>
    );    
}
  • 多使用解构,如 Function Component 的 props
const MenuItem = ({
    menuId, menuText, onClick, activeId,
}) => {
    function onItemClick() {
        onClick(menuId);
    }

    return (
        <div
            menuId={menuId}
            className={`${style} ${activeId === menuId ? active : ''}`}
            onClick={onItemClick}
        >
            {menuText}
        </div>
    );
};
  • 如果 props 的数据不会改变,就不需要在 state 或者组件实例属性里拷贝一份

经常会看见这样的代码:

componentWillReceiveProps(nextProps) {
    this.setState({num: nextProps.num});
}

render() {
    return(
        <div>{this.state.num}</div>
    );
}

num 在组件中不会做任何的改变,这种情况下直接使用 this.props.num 就可以了。

  • 遵循单一职责原则,使用 HOC / 装饰器 / Render Props 增加职责

比如一个公用的组件,数据来源可能是父组件传过来,又或者是自己主动通过网络请求获取数据。这时候可以先定义一个纯展示型的 Function Component,然后再定义一个高阶组件去获取数据:

function Comp() {
    ...
}

class HOC extends PureComponent {

    async componentDidMount() {
        const data = fetchData();
        this.setState({data});
    }
    
    render() {
        return (<Comp data={this.state.data}/>);
    }
}
  • 组合优于继承

笔者在真实项目中就用过 2 次以继承的形式写组件,自己写得很爽,代码的复用性也很好,但最大的问题是别人看不懂。我将复用的业务逻辑和 UI 模版都在父类定义好,子类只需要传入一些参数,然后再覆盖父类的几个方法就好(render的时候会用到)。简化的代码如下:

class Parent extends PureComponent {
    componentDidMount() {
        this.fetchData(this.url);
    }
    
    fetchData(url) {
        ...
    }
    
    render() {
        const data = this.calcData();
        return (
            <div>{data}</data>
        );
    }
}

class Child extends Parent {
    constructor(props) {
        super(props);
        this.url = 'http://api';
    }
    
    calcData() {
        ...
    }
}

这样的写法从语言的特性和功能实现来说,没有任何问题,最大的问题是不符合 React 的组件编写习惯。父类或者子类肯定有一方是不需要实现 render 方法的,而一般我们看代码都会优先找 render 方法,找不到就慌了。另外就是搞不清楚哪些方法是父类实现的,哪些方法是子类实现的,如果让其他人来维护这份代码,会比较吃力。

继承会让代码难以溯源,定位问题也比较麻烦。所有通过继承实现的组件都可以改写为组合的形式。上面的代码就可以这样改写:

class Parent extends PureComponent {
    componentDidMount() {
        this.fetchData(this.props.url);
    }
    
    fetchData(url) {
        ...
    }
    
    render() {
        const data = this.props.calcData(this.state);
        return (
            <div>{data}</data>
        );
    }
}

class Child extends PureComponent {
    calcData(state) {
        ...
    }
    
    render() {
        <Parent url="http://api" calcData={this.calcData}/>
    }
}

这样的代码是不是看起来舒服多了?

三、总结

本文列举了笔者在项目实战和 Code Review 过程中总结的一些最佳实践,只代表个人立场。理解并遵循这些最佳实践,写出来的代码质量都有一定的保证。如果你有不同的意见,或者有补充的最佳实践,欢迎留言。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Beginning Apache Struts

Beginning Apache Struts

Arnold Doray / Apress / 2006-02-20 / USD 44.99

Beginning Apache Struts will provide you a working knowledge of Apache Struts 1.2. This book is ideal for you Java programmers who have some JSP familiarity, but little or no prior experience with Ser......一起来看看 《Beginning Apache Struts》 这本书的介绍吧!

html转js在线工具
html转js在线工具

html转js在线工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具