React组件介绍

组件介绍

React中组件主要可分为函数组件和类组件,两者区别是函数组件没有state和生命周期,故函数组件也称为 stateless functional components, 适用于仅进行简单渲染操作的组件。
此外,还有高阶组件HOC(higher-order component), 即高阶函数,接收一个React组件作为参数,然后返回另外的React组件,通常是原组件的加强版。

函数组件

可接收props,返回一个JSX元素,例如

function ReactHeader(props) {
  return <h1>React {props.version} Documentation</h1>
}

<ReactHeader version={18}></ReactHeader>

经过Babel转义后为

function ReactHeader(props) {
  return React.createElement(
    "h1",
    null,
    "React ",
    props.version,
    " Documentation"
  );
}

React.createElement(ReactHeader, { version: 18 });
函数组件参数初始化
  1. 在函数组件上添加一个名为defaultProps的静态属性
function ThemedButton(props) {
  const { theme, label, ...restProps } = props;
  return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}

// 设置默认属性
ThemedButton.defaultProps = {
  theme: "secondary",
  label: "Button Text"
};
  1. 使用ES6的对象解构语法
// METHOD 1:
function ThemedButton(props) {
  const { theme = 'secondary', label = 'Button Text', ...restProps } = props;
  return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}

// METHOD 2:
// 更加紧凑的方式
function ThemedButton({ theme = 'secondary', label = 'Button Text', ...restProps }) {
  return <button className={`btn btn-${theme}`} {...restProps}>{ label }</button>
}

类组件

可以使用ES6的extends写法,15.4之前可利用React.creatClass创建类组件,但如今已经弃用了。
关于两者区别可参考这里
考虑到props是不建议修改的,React推荐数据源从上往下流,仅有一个被trusted的数据源,其余由dispatch发送事件,subscribe订阅事件, store存储所有信息,该思想为flux。为了追踪一些状态或变量,React在class中提供了state对象,用于存储组件私有变量,对state的改变则通过setState进行

示例:

class simple extends React.Component {
  constructor(){
    super(props)
    this.state = {
        status: ''
    }
  }
  render() {
    return (
      <h1>Simple test</h1>
    )
  }
}
console.log(<simple />); 

经过Babel转义后

var simple = function (_React$Component) {
  _inherits(simple, _React$Component);

  function simple() {
    _classCallCheck(this, simple);

    var _this = _possibleConstructorReturn(this, (simple.__proto__ || Object.getPrototypeOf(simple)).call(this, props));

    _this.state = {
      status: ''
    };
    return _this;
  }

  _createClass(simple, [{
    key: 'render',
    value: function render() {
      return React.createElement(
        'h1',
        null,
        'Simple test'
      );
    }
  }]);

  return simple;
}(React.Component);

console.log(React.createElement('simple', null));
类组件初始化

类组件的初始化也可在组件上添加一个名为defaultProps的静态属性, 定义一个名为Component的组件后

Component.defaultProps = {
  name: "admin",
  age: 20
};

或者

//在组件内设置(与constructor平级)
static defaultProps = {
    name: 'com',
    age: 18
  }
组件参数类型检测

组件的类型检测需要额外引入prop-types

  <script src="./prop-types.js"></script>

然后在组件上应用propTypes

Component.propTypes = {
  name: PropTypes.string,
  age: PropTypes.number    
}

关于类型检测更多种类可参考这里

组件this绑定

与ES6中类语言一致,通过extends得到的class,组件内的方法将不会自动将this绑定到实例上,有三种方式可以进行绑定

实例:

constructor(props){
    super(props)
    this.state = {
      status: 'logged',
      message: ''
    }
    this.handleChange = this.handleChange.bind(this)
  }
  handleChange(e){
    console.log('handle Change')
    console.log(this); // 在constructor中通过bind绑定
    this.setState({message:e.target.value})
    console.log(this.state.message)
  }
  handleFocus(){  
      console.log('handle Focus')
      console.log(this); // 在render的时候通过bind绑定
  }
  handleBlur = ()=>{  
      console.log('handle Blur')
      console.log(this); // 用箭头函数自动绑定
  }
  render(){
    console.log(this.props)
    return (
      //写style是需要加双括号,JSX编码格式要求,style必须是一个对象
      <div style={{color:"blue"}}>我是Component组件 &gt;&gt; {undefined}
        <p>{this.props.children}</p>
        <p>Name: {this.props.name}</p>
        <p>Age: {this.props.age}</p>
        <p>Status: {this.state.status}</p>
        <p>Message: {this.state.message}</p>
        <input type="text" value={this.state.message} onChange={this.handleChange} 
                              onFocus={this.handleFocus.bind(this)}  
                              //第一、三种写法的性能一样,在实例化时会调用
                              //但万一我们需要改变语法结构,第一种方式可以完全不需要去改动 JSX 的部分:
                              //第二种则会在触发render时不断进行调用,性能不太好
                              onBlur={this.handleBlur}/>
      </div>
    )
  }

另外可通过以下方式,将函数组件转为类组件
a. 通过class 声明与函数同名的类,该类继承React.Component
b. 添加一个空方法,名为render
c. 将函数主体移入render方法中
d. 将render中的props替换为this.props
e. 将props中需要修改的data移入state中
f. 在类的render中,将this.props.data替换为this.state.data
g. 创建一个class constructor 用于初始化this.state

类组件生命周期

React类组件生命周期有较大的变动,我写这篇文章的时候是V16.7,V16.3开始官方已经申明以下生命周期函数会在下一个大版本停用,为了兼容性,在这些过时的生命周期函数前加前缀UNSAFE__

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate
    取而代之的
  4. static getDerivedStateFromProps
    取代componentWillReceiveProps
    特点是纯函数,且由于是静态方法故无法获取this
  5. getSnapshotBeforeUpdate
    可取代componentWillUpdate
    官方提出改动一是WillMount争议较大,可参见这里存在滥用情况多,去除后两个是为了以后增加异步渲染的过程。

有一种错觉,在componentWillMount请求的数据在render就能拿到,但其实render在willMount之后几乎是马上就被调用,根本等不到数据回来,同样需要render一次“加载中”的空数据状态,所以在didMount去取数据几乎不会产生影响。另外,react只能保证componentDidMount-componentWillUnmount成对出现,componentWillMount可以被打断或调用多次,因此无法保证事件监听能在unmount的时候被成功卸载,可能会引起内存泄露

以下是新旧生命周期示意图对比
[图片上传中...(React_lifecycle_old.png-b3c3f5-1546961611183-0)]

[图片上传中...(React_lifecycle_new.png-68ca8-1546961671751-0)]

建议用法:

  1. constructor初始化state
  2. componentDidMount
    此过程请求异步加载的数据
    并添加事件监听
  3. getDerivedStateFromProps(nextProps,preState)

根据props更新之前的state,若需要在Setstate前进行对比props是否有更新,需在shouldComponentUpdate中使用

if (nextProps.currentRow !== prevState.lastRow) {
  return {
    ...
    lastRow: nextProps.currentRow,
  };
  // 不更新state
  return null
}
  1. componentDidUpdate
    处理由state或props更新触发的请求

  2. getDerivedStateFromProps
    传入新的props重新异步读取数据

  static getDerivedStateFromProps(nextProps, prevState) {
    // Store prevId in state so we can compare when props change.
    if (nextProps.id !== prevState.prevId) {
      return {
        externalData: null,
        prevId: nextProps.id,
      };
    }
    // No state update necessary
    return null;
  }
  componentDidUpdate(prevProps, prevState) {
    if (this.state.externalData === null) {
      this._loadAsyncData(this.props.id);
    }
  }

参考部分1
[参考部分2]

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容