React系列---React(二)组件的prop和state

React系列---React(一)初识React
React系列---React(二)组件的prop和state
React系列---React(三)组件的生命周期react


组件是React的基石,全部的React应用程序都是基于组件的。基于组件的应用开发是普遍使用的软件开发模式,用分而治之的方法,把一个大的应用分解成若干小的组件,每一个组件只关注某个特定功能,可是把组件组合起来,就能构成一个功能庞大的应用。npm

React组件的数据分为两种,prop和state,不管prop或者state的改变,均可能引起组件的从新渲染。prop是组件对外接口,state是组件内部状态。segmentfault

第一个组件

create-react-app工具,初始化一个React项目:数组

npm create-react-app react-component-demo

建立一个能够计算点击数的组件:
/src/ClickCounter.js:浏览器

import React from 'react';

class ClickCounter extends React.Component {
    constructor(props) {
        super(props);
        this.onClickButton = this.onClickButton.bind(this);
        this.state = {count: 0};
    }

    onClickButton() {
        this.setState({count: this.state.count + 1});
    }

    render() {
        return (
          <div>
            <button onClick={this.onClickButton}>Click Me</button>
            <div>
              Click Count: {this.state.count}
            </div>
          </div>
        );
    };
}

export default ClickCounter;

修改/src/index.js:babel

import React from 'react';
import ReactDOM from 'react-dom';
import ClickCounter from './ClickCounter';

ReactDOM.render(<ClickCounter />, document.getElementById('root'));

运行React项目app

npm run start

clipboard.png

点击按钮,数字会随之增长。恭喜你,已经构建了一个有交互的组件!less

咱们还能够在React组件中定义样式。修改ClickCounter组件的render函数:dom

render() {
    const counterStyle = {
        margin: '16px'
    };
    return (
      <div style={counterStyle}>
        <button onClick={this.onClickButton}>Click Me</button>
        <div>
          Click Count: <span id="clickCount">{this.state.count}</span>
        </div>
      </div>
    );
};

组件的prop

React组件经过定义本身可以接受的prop就定义了本身对外公共接口。外部世界经过prop和组件对话。函数

给prop赋值

从外部世界看prop的使用:

<SampleButton id="sample" borderWidth={2} onClick={onButtonClick} style={{color: "red"}} />

上面的例子使用了名为SampleButton的组件实例。React组件的prop所能支持的类型除了字符串,还能够是任何一种JavaScript语言支持的数据类型。当prop的类型不是字符串时,再JSX中必须用花括号{}把值包裹,因此style的值有两层花括号,外层表明是JSX的语法,内层表明这是个对象常量。

React组件要反馈数据给外部世界,也是用prop,由于prop类型也能够是函数,函数类型的prop等于让父组件交给子组件一个回调函数,子组件在恰当的时机调用函数的prop,就能够把信息传递给外部世界。

为了演示,咱们构造一个应用包含两种组件,ControlPanel父组件,而后若干个Counter子组件。对于Counter组件,父组件ControlPanel就是外部世界:

class ControlPanel extends React.Component {
    render() {
        return (
          <div>
            <Counter caption="First" initValue={0} />
            <Counter caption="Second" initValue={10} />
            <Counter caption="Third" initValue={20} />
          </div>
        );
    }
}

React要求render只能返回一个元素,因此咱们用div包裹了3个子组件。

每一个Counter组件使用了caption和initValue两个prop。ControlPanel经过caption的prop传递给Counter组件实例说明文字,经过initValue的prop传递给Count组件一个初始的计数值。

读取prop值

看下Counter组件内部是如何接收prop的:

class Counter extends React.Component {
    constructor(props) {
        super(props);

        this.onClickIncrementButton = this.onClickIncrementButton.bind(this);
        this.onClickDecrementButton = this.onClickDecrementButton.bind(this);

        this.state = {
            count: props.initValue || 0
        };
    }
}

若是组件须要定义本身的构造函数,构造函数第一行必定要经过super调用父类React.Component的构造函数。给this.props赋值也是React.Component构造函数的工做之一。
在Counter的构造函数中,还给两个成员函数绑定了当前this的执行环境,由于ES6方式建立的组件并不自动给咱们绑定this到当前实例对象。

在其余函数中则能够经过this.props访问传入的值,看一下render函数:

render() {
    const {caption} = this.props; // ES6的解构赋值
    return (
      <div>
        <button style={buttonStyle} onClick={this.onClickIncrementButton}>+</button>
        <button style={buttonStyle} onClick={this.onClickDecrementButton}>-</button>
        <span>{caption} count: {this.state.count}</span>
      </div>
    );
};

propTypes检查

在ES6方法定义的组件中,能够经过增长类的propTypes属性来定义prop规格。在运行和静态代码检查时,均可以根据propTypes判断外部世界是否正确地使用了组件的属性。

增长Counter组件的propTypes定义:

Counter.propTypes = {
  caption: PropTypes.string.isRequired,
  initValue: PropTypes.number
};

开发过程当中,定义propTypes代码能够避免犯错,可是在发布产品时,能够用babel-react-optimize工具自动去除propTypes,这样部署到产品环境的代码就会更优。

组件的state

驱动组件渲染的除了prop,还有state,state表明组件内部状态。因为React组件禁止修改传入的prop,因此当组件须要记录自身的数据变化时,就要使用state。
在Counter组件中,初始计数能够经过initValue这个prop指定。当用户点击“+”和“-”改变计数时,就要Counter组件本身经过state来存储了。

初始化state

一般在构造函数的结尾处初始化state,就如上面的Counter:

constructor(props) {
    ...
    this.state = {
        count: props.initValue || 0
    };
}

因为在PropType声明中没有用isRequired,咱们须要在代码中判断给定的prop值是否存在,不存在则给一个默认值。咱们能够利用React的defaultProps功能,避免判断逻辑这种充斥在构造函数之中,让代码更优。

给Counter组件添加defaultProps代码:

Counter.defaultProps = {
    initValue: 0
};

构造函数就能够简化了:

constructor(props) {
    ...
    this.state = {
        count: props.initValue
    };
}

读取和更新state

经过给button的onClick属性挂载点击事件处理函数,咱们能够改变组件的state,以点击“+”按钮的响应函数为例:

onClickIncrementButton() {
    this.setState({count: this.state.count + 1});
}

经过this.state能够读取到组件的当前state。注意的是,改变state必须使用this.setState函数,而不能直接修改this.state。若是你违反这个操做,浏览器Console会告警。

直接修改this.state的值,只是野蛮的修改了state,却没有驱动组件从新渲染,新的值固然也不会反应在界面上。而this.setState()函数所作的事情,就是改变this.state的值后再驱动组件从新渲染。

无状态函数式组件

没有内部state,不须要组件生命周期函数。能够用纯函数的形式来表达。它作的事情只是根据输入来展现组件,没有其余反作用。能够把这种组件称为无状态函数式组件(stateless functional component)。

import React from 'react';

// 用一个纯函数表示
const Hobby = (props) => <li>{props.hobby}</li>;

export default Hobby;

state设计原则

建立尽可能多的无状态组件,这些组件惟一关心的就是渲染数据。而在最外层,应该有一个包含state的父级别组件,用于处理各类事件、交流逻辑、修改state。对应的子组件要关心的只是传入的属性而已。

state应该包含组件的事件回调函数可能引起UI更新的这类数据。在实际的项目中,应该是轻量化的JSON数据,尽可能把数据的表现设计到最小,更多的数据能够在render中经过各类计算获得。

prop和state对比

  • prop用于定义外部接口,state用于记录内部状态;
  • prop的赋值在外部世界使用组件时,state的赋值在组件内部;
  • 组件不该该改变prop的值,而state的存在的目的就是让组件来改变的。

DOM操做

大多数状况下,不须要操做DOM去更新UI,应使用setState。可是有些状况确实须要访问一些DOM(如表单的值),那么可采用refs方式来得到DOM节点。只须要加个ref属性,而后经过this.refs.name来得到对应的DOM结构。

示例Profile组件:

render() {
  return (
    <div>
      ...
      <input type="text" ref="hobby" />
      <button onClick={this.addHobbyCallback}>添加爱好</button>
    </div>
  )
}

在button上添加事件,取得input的值,添加到state的值里面:

addHobbyCallback() {
  // 用this.refs.name来取得DOM节点
  let hobbyInput = this.refs.hobby;
  let val = hobbyInput.value;
  if (val) {
    let hobbies = this.state.hobbies;
    // 添加值到数组
    hobbies = [...hobbies, val];
    // 更新state, 刷新UI
    this.setState({
      hobbies
    }, () => {
      hobbyInput.value = '';
    });
  }
}

React系列---React(一)初识React
React系列---React(二)组件的prop和state
React系列---React(三)组件的生命周期

相关文章
相关标签/搜索