菜单

React组件的二种写法计算

2020年4月2日 - 新闻中心

前言

React组件的三种写法总结,react组件写法

React 专注于 view 层,组件化则是 React
的基础,也是其核心理念之一,一个完整的应用将由一个个独立的组件拼装而成。

截至目前 React 已经更新到 v15.4.2,由于 ES6
的普及和不同业务场景的影响,我们会发现目前主要有三种创建 React
组件的写法:1. ES5写法React.createClass,2. ES6写法React.Component,3.
无状态的函数式写法(纯组件-SFC)。

你们最钟爱哪种写法呢?萝卜青菜各有所爱~
每个团队都有自己的代码规范和开发模式,但书写 React 组件时
都会以提高代码阅读性、更优的组件性能、易于 bug
追踪为原则。下面我们就聊聊这三种写法的区别,以及各自所适用场景的最佳实践。

ES5-写法 React.createClass

React.createClass不用多说,我们最早使用这个方法来构建一个组件“类”,它接受一个对象为参数,对象中必须声明一个render方法,render返回一个组件实例,下面用一个
SwitchButton 组件的例子来看看React.createClass的具体用法:

var React = require('react');
var ReactDOM = require('react-dom');
var SwitchButton = React.createClass({
 getDefaultProp:function() {
 return { open: false }
 },
 getInitialState: function() {
 return { open: this.props.open };
 },
 handleClick: function(event) {
 this.setState({ open: !this.state.open });
 },
 render: function() {
 var open = this.state.open,
  className = open ? 'switch-button open' : 'btn-switch';

 return (
  <label className={className} onClick={this.handleClick.bind(this)}>
  <input type="checkbox" checked={open}/>
  </label>
 );
 }
});
ReactDOM.render(
 <SwitchButton />,
 document.getElementById('app')
);

ES6-写法 React.Component

React 升级到 v0.13 后就支持了 ES6 的class语法,我们可以使用class App
extends
React.Component{…}的方式创建组件,这也是目前官方推荐创建有状态组件的方式。用
ES6 重写上面 SwitchButton 组件的例子:

import React from 'react'
import { render } from 'react-dom'
class SwitchButton extends React.Component {
 constructor(props) {
 super(props)
 this.state = {
  open: this.props.open
 }
 this.handleClick = this.handleClick.bind(this)
 }
 handleClick(event) {
 this.setState({ open: !this.state.open })
 }
 render() {
 let open = this.state.open,
  className = open ? 'switch-button open' : 'btn-switch'
 return (
  <label className={className} onClick={this.handleClick}>
  <input type="checkbox" checked={open}/>
  </label>
 )
 }
}
SwitchButton.defaultProps = {
 open: false
}
render(
 <SwitchButton />,
 document.getElementById('app')
)

与React.createClass创建组件的不同之处:

import

与这里使用了 ES6 的import语句替代require()方法导入模块,其中import
{render}可以直接从模块中导入变量名,这种写法更加简洁直观。

初始化 state

React 使用 ES6 的“类”继承实现时,去掉了getInitialState这个 hook
函数,state的初始化放在构造函数方法constructor中声明。

this 绑定

React.Component创建组件时,事件函数并不会自动绑定this,需要我们手动绑定,不然this将不会指向当前组件的实例对象。以下有三种绑定this的方法:

1.
在constructor中使用bind()进行硬绑定

constructor() {
 this.handleClick = this.handleClick.bind(this);
}

2. 直接在元素上使用bind()绑定

<label className={className} onClick={this.handleClick.bind(this)}>

3. ES6 有个很有用的语法糖:Arrow
Function(箭头函数)它可以很方便的使this直接指向class
SwitchButton(它的作用等同于大家熟悉的var self =
this,但后者会让代码变得混乱,Arrow Function 就很好的解决了这一问题)

<label className={className} onClick={()=>this.handleClick()}>

无状态的函数式写法(纯组件 SFC)

React.createClass和React.Component都可以用来创建有状态的组件,而
无状态组件 – Stateless Component 是 React 在 v0.14 之后推出的。

它的出现
是因为随着应用复杂度不断提升和组件本数量的增加,组件按各自职责被分成不同的类型,于是有一种只负责展示的纯组件出现了,它的特点是不需要管理状态state,数据直接通过props传入,这也符合
React 单向数据流的思想。

对于这种无状态的组件,使用函数式的方式声明,会使得代码的可读性更好,并能大大减少代码量,Arrow
Function 则是函数式写法的最佳搭档:

const Todo = (props) => (
 <li
 onClick={props.onClick}
 style={{textDecoration: props.complete ? "line-through" : "none"}}
 >
 {props.text}
 </li>
)

上面定义的 Todo
组件,输入输出数据完全由props决定,而且不会产生任何副作用。对于props为
Object 类型时,我们还可以使用 ES6 的解构赋值:

const Todo = ({ onClick, complete, text, ...props }) => (
 <li
 onClick={onClick}
 style={{textDecoration: complete ? "line-through" : "none"}}
 {...props}
 >
 {props.text}
 </li>
)

无状态组件一般会搭配高阶组件(简称:OHC)一起使用,高阶组件用来托管state,Redux
框架就是通过 store
管理数据源和所有状态,其中所有负责展示的组件都使用无状态函数式的写法。

这种模式被鼓励在大型项目中尽可能以简单的写法
来分割原本庞大的组件,而未来 React
也会面向这种无状态的组件进行一些专门的优化,比如避免无意义的检查或内存分配。所以建议大家尽可能在项目中使用无状态组件。

当然,无状态组件也不是万金油,比如它不支持”ref”,原因很简单,因为 React
调用它之前,组件不会被实例化,自然也就没有”ref”,(ref和findDOMNode实际上打破了父子组件之间仅通过
props 来传递状态的约定,违背了 React 的原则,需要避免)。

以上三种写法的比较,以及最佳实践

Facebook 官方早就声明 ES6React.Component将取代React.createClass。随着
React 不断发展,React.createClass暴露出一些问题:

总的来说:无状态函数式写法
优于React.createClass,而React.createClass优于React.Component。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持帮客之家!

React
专注于 view 层,组件化则是 React
的基础,也是其核心理念之一,一个完整的应用将由一个个独…

最近项目基本都是用 React,今天总结分享 React Component
常见的几种形式,如果你在写 React
时经常不知道怎么拆分代码,这篇文章或许对你有所帮助。

React.Component是一个抽象基类。这意味着直接引用React.Component是毫无意义的。你可以实现一个它的子类,并且至少定义一个render()方法即可使用。

为了更充分理解 React,先搞懂平时写的 JSX
是什么。初学的时候有比较大困惑,这是一门新语言吗?大部分人是匆匆扫过文档就开始开发。通过
babel-presets-react 处理能看到,其实 JSX
只是语法糖,最终在浏览器跑的还是 JS。React Component 最终都通过
React.createElement 创建。 总之,写 React 其实就是在写 JS 。

SFC (Stateless Functional Component)

React 可以使用 Function 来创建 Component,这类 Component 没有 lifecycle,
内部不维护 state,只要传入的 props 有变化则进行重新渲染。

function Welcome {return Hello, {props.name};}

用箭头函数的写法还更加简洁。

const Welcome = props => Hello, {props.name};

上面两种形式生成 es5 代码都是一样的。

var Welcome = function Welcome {return _react2.default.createElement("h1",null,"Hello, ",props.name);};

SFC 的特点是纯粹只做 render,代码简短没有其他条件分支,并且相比 class
Component 编译后的代码量会少一些。

尴尬的是,在 React 16.7 react hooks 出来之后,SFC
这个名字有歧义了,因为用上 useState,SFC 也可以有 local state,
同样可以拥有 lifecycle。再称之为 Stateless Components 就很尴尬,改名叫
FC ?

HOC (Higher-Order Components)

高阶组件对于 Vue 开发者来说应该是个陌生的概念(不知道,我用 Vue
的时候没见过类似的用法)。从代码上看,高阶组件就是一个方法,传入一个组件,返回另一个组件。

function logProps {return class extends React.Component {componentWillReceiveProps {console.log('Current props: ', this.props);console.log('Next props: ', nextProps);}render() {return ;}}}

最常见的高阶组件是 react-redux 里面的 connect 方法,通过传入 组件和
map*ToProps 方法,让组件和 store 连接。组件内部就可以直接通过 props
获得 connect 之后的值。

exprot default connect(mapStateToProps,mapDispatchToProps,);

高阶组件适合用来扩展功能,把这部分功能从业务组件中抽离出来,需要的套上,不需要的时候移除,对被包裹组件侵入性非常小。

Dynamic Component

有些业务场景下,在执行时才能确定具体的标签或者组件是什么。在 React
的世界里面,以大写字母开头会被当成动态组件加载,而小写字母开头会被认为是
HTML DOM tag。

// Heading.jsrender() {const { tag: Tag, children } = this.props;return { children }}

根据万物皆为 JS 理论,只要传入不同的 tag 标签,就会渲染出不同的 heading
标签。

我们常用这种方式,在后端配置组件和数据,前端读取配置之后渲染出对应的内容。

FaCC(Functions as Child Components)

React children 还可以是 Function 类型,如果直接调用它会什么写法?

比如封装一个 Loading 组件,会给 children 提供 loading
参数,业务组件再根据 loading 判断需要 render 什么内容。

class LoadArea extends Component {state = {loading: true,};componentDidMount.then => {this.setState.catch => {this.setState}render() {return ({this.props.children({...this.props,...this.state,})});}}

render() { => {loading? : }}

同样的,最终执行时都是 JS,没有什么好奇怪的。

React 16.* 新版本的 Conext.Consumer 就是采用了这种写法。

render() {...{ => (Toggle Theme)}... }

再以最近开发的例子,分享组件拆分的好处。

需求:开发倒计时组件,运营配置倒计时结束时间,倒计时初始化时间从服务端获取,结束之前显示倒计时,倒计时结束之后做对应的操作,比如切换倒计时为其他组件。

一个业务层容器组件,负责统筹,处理业务逻辑。
一个通用‘倒计时’的组件,向服务端轮询系统时间,计算当前剩余时间,FaCC
的形式提供给 children。 一个倒计时UI组件,对剩余时间格式化以及 UI 展示。

// CountDownContainer.jsrender() {const {endTime,renderSomethingAfterCountDown,} = this.props;return ({seconds => (seconds > 0? : renderSomethingAfterCountDown});}

// TimeLeftProvider.jsexport default class TimeLeftProvider extends PureComponent {static propTypes = {children: PropTypes.func,endTime: PropTypes.number,}// ...componentDidMount;}poll;this.pollTimer = setInterval => {queryServerTime();}, pollInterval * 1000);}countDown => {this.setState(prevState => ({remainingSeconds: prevState.remainingSeconds - 1,}));}, 1000);}render() {const { remainingSeconds, reliable } = this.state;return this.props.children(remainingSeconds, reliable);}}

// CountDown.jsfunction CountDown {const {remainingSeconds,} = props;const numbers = formatSeconds;const inputs = ['days', 'hours', 'minutes', 'seconds'];return ({inputs.map(key => ({label: key,number: numbers[key],})).map;} 代码结构清晰,组件之间各司其职。 组件可复用性强。 单元测试简单,每个组件都只测试自身的逻辑。总结以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图