React.js基础手册

React简介

(1)简介

React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了。

React主要用于构建UI。你可以在React里传递多种类型的参数,如声明代码,帮助你渲染出UI、也可以是静态的HTML DOM元素、也可以传递动态变量、甚至是可交互的应用组件。

React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它。

(2)特点

  • 声明式设计 −React采用声明范式,可以轻松描述应用。

  • 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。Virtual DOM

  • 灵活 −React可以与已知的库或框架很好地配合。

  • JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。

  • 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。

  • 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

(3)相关js库

react.js: React的核心库
react-dom.js: 提供操作DOM的扩展库
babel.min.js: 解析JSX语法代码转为纯JS语法代码的库

React安装

1、下载

React可以直接下载使用,下载官网 http://facebook.github.io/react/

2、安装

(a)通过npm使用React

​ 操作系统需要支持Node.js,建议在React中使用CommonJS模块系统,比如browserify 或 webpack,本教程使用 webpack。

​ 国内使用npm速度较慢,使用淘宝定制的cnpm(gzip压缩支持)命令行工具代替默认的npm:

$ npm install -g cnpm --registry=https://registry.npm.taobao.org
$ npm config set registry https://registry.npm.taobao.org
(b)使用create-react-app快速构建开发环境

create-react-app来自于Facebook,通过该命令无需配置就能快速构建React开发环境。

create-react-app自动创建的项目是基于Webpack+ES6。

推荐在C盘以外的盘符创建一个空文件夹,比如定义:ggReact ,然后通过cmd小黑窗定位到我们新建的ggReact文件夹再开始 npm 等一系列操作,操作步骤大概如下:

$ cnpm install -g create-react-app  
$ create-react-app my-app   /*创建项目*/
$ cd my-app/    /*进入项目目录*/
$ npm start     /*启动项目*/
createReact01.png
createReact02.png
createReact03.png

在浏览器中打开http://localhost:3000/ ,显示结果如下:

firstResult.png

React第一个项目

1、React项目目录结构

项目结构.png

(1)node_modules: 这里面包含了react项目中会用到的一些组件,install的时候下载下来的,你可以进去看看,有一些如base64之类的我们可能会用到的组件;

(2)public:里面包含了我们项目中的启动页面,react比较适合单页面项目应用开发,所以暂时只包含一个index.html,并且这也是react工程的入口页面,入口页面怎么理解,就是你启动项目后,打开的首页,第一眼呈现的那个页面;

(3)src:里面包含了一些我们自己使用的js文件,css文件,img文件等等,但你打开src文件夹你会发现很混乱,不要管什么app.js,你就看到index.js即可,系统默认将index.html对准了index.js,index.js也就是我们的入口js,他和index.html所对应。

2、让目录结构更易懂

接下来,我们改动一下src里面的内容和结构,让首次接触react的小伙伴尽量能觉得他有点亲切的感觉,事实证明,越亲切的事物我们越希望去接近。

src文件夹内容修改如图:

项目结构1.png

src文件夹下原来的所有东西,除了index.js之外,都可以删掉,这样就可以轻装上阵了

接下来,我们做一些代码修改,展示一个含有样式的hello world示例。

  • 看public下的index.html,我们还是保留最简洁,最易懂的部分,如图:
项目结构2.png
  • 再来看src下的index.js,我们依然保留最简约的部分,如图:
项目结构3.png
  • 接下来我们为这个demo添加样式,还是那条原则,尽量少写行内样式,写自己的css文件。这个时候我们在 src文件夹下的css文件夹下创建demo.css,写入css样式如图:
项目结构4.png
  • 在index.js文件中使用import关键字引入demo.css,如图:
项目结构5.png
  • 看一眼我们的成果,如图:
项目结构6.png

React JSX

1、JSX介绍

全称: JavaScript XML, React 使用 JSX 来替代常规的 JavaScript。

JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

我们不需要一定使用 JSX,但它有以下优点:

  • JSX 执行更快,因为它在编译为 JavaScript 代码后进行了优化。
  • 它是类型安全的,在编译过程中就能发现错误。
  • 使用 JSX 编写模板更加简单快速。
  • 作用: 用来创建react虚拟DOM(元素)对象

    • var ele = <h1>Hello JSX!</h1>;
    • 注意1: 它不是字符串, 也不是HTML/XML标签
    • 注意2: 它最终产生的就是一个JS对象
  • 标签名任意: HTML标签或其它标签

  • 标签属性任意: HTML标签属性或其它

  • 基本语法规则

    • 遇到 <开头的代码, 以标签的语法解析: html同名标签转换为html同名元素, 其它标签需要特别解析
    • 遇到以 { 开头的代码,以JS的语法解析: 标签中的js代码必须用{ }包含
  • babel.js的作用

    • 浏览器的js引擎是不能直接解析JSX语法代码的, 需要babel转译为纯JS的代码才能运行
    • 只要用了JSX,都要加上type="text/babel", 声明需要babel来处理

2、使用 JSX

JSX 看起来类似 HTML ,我们可以看以下实例:

html文件:

  <body>
    <div id="root"></div>
  </body>

js文件:

ReactDOM.render(
    <h1>Hello, world!</h1>,
    document.getElementById('root')
);

注意:代码中能够嵌套多个 HTML 标签,但需要使用一个标签元素包裹它

  • 错误例子:
ReactDOM.render(
  <h1>这是错误的例子</h1>
  <span>假设这里是标题下面的内容</span>,
  document.getElementById("example")
);
  • 正确例子:
ReactDOM.render(
  <section>
    <h1>这是正确的例子</h1>
    <span>假设这里是标题下面的内容</span>
  </section>,
  document.getElementById("example")
);

3、独立文件

如果需要使用公共样式,可以将React JSX 代码可以放在一个独立文件上,并在不同的js文件中引入即可,如下:

AppTop.js文件中:

import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import '../css/demo.css';

//类定义
class AppTop extends Component {
    render() {
        return (
            <header>
                <h1>我是公共标题栏</h1>
            </header>
        );
    }
}

export default AppTop; //将自定义的DOM元素暴露出来,供给其他的页面使用

index.js文件中:

import React from 'react';
import ReactDOM from 'react-dom';
import AppTop from './js/AppTop.js';

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

4、JavaScript 表达式

我们可以在 JSX 中使用 JavaScript 表达式。表达式写在花括号 {} 中。实例如下:

ReactDOM.render(
    <div>
      <h1>{1+1}</h1>
    </div>,
    document.getElementById('example')
);

在 JSX 中不能使用 if else 语句,但可以使用 三元运算 表达式来替代。

var i = 0;
ReactDOM.render(
    <div>
      <h1>{i == 1 ? 'True!' : 'False'}</h1>
    </div>,
    document.getElementById('example')
);

5、样式

React 推荐使用内联样式。React 会在指定元素数字后自动添加 px

var myStyle = {
    fontSize: 100,  //等价于fontSize: '100px',
    color: '#00ff00'
};
ReactDOM.render(
    <h1 style = {myStyle}>我是标题</h1>,
    document.getElementById('example')
);

6、注释

注释需要写在花括号中,需要注意的是:

  • 在标签内部的注释需要花括号
  • 在标签外的的注释不能使用花括号
ReactDOM.render(
    /*标签外部的注释 */
    <h1>我是标题 {/*标签内部的注释*/}</h1>,
    document.getElementById('example')
);

7、数组

JSX 允许在模板中插入数组,数组会自动展开所有成员

var arr = [
  <h1>HTML</h1>,
  <h2>CSS</h2>
];
ReactDOM.render(
  <div>{arr}</div>,
  document.getElementById('example')
);

8、HTML 标签 vs React 组件

React 可以渲染 HTML 标签 (strings) 或 React 组件 (classes)。React JSX 使用大、小写的约定来区分本地组件的类和 HTML 标签。

  • 要渲染 HTML 标签,只需在 JSX 里使用小写字母的标签名。
var my = <h1>我是标题呀</h1>;
ReactDOM.render(my, document.getElementById('example'));
  • 要渲染 React 组件,只需创建一个大写字母开头的本地变量。(大驼峰法)
class My extends React.Component {
    render() {
        return (<h1>标题1</h1>);
    }
}

ReactDOM.render(<My />, document.getElementById('example'));

//var my = <My />;
//ReactDOM.render(my, document.getElementById('example'));

React 组件

React.js 中一切皆组件,用 React.js 写的其实就是 React.js 组件。

我们在编写 React.js 组件的时候,一般都需要继承 React.js 的 Component(类定义)。一个组件类必须要实现一个 render 方法,这个 render 方法必须要返回一个 JSX 元素。但这里要注意的是,必须要用一个外层的 JSX 元素把所有内容包裹起来。返回并列多个 JSX 元素是不合法的。

1、定义单个组件

(1)定义组件
  • 方式1:通过React自定义组件(DOM元素):类定义
class MyApp extends React.Component {
    render() {
        return (
            <div>
                <h2>这是标题2</h2>
                <h3>这是标题2</h3>
            </div>
        );
    }
}
export default MyApp;
  • 方式2:通过React自定义组件(DOM元素):函数定义
const MyApp = () => <div><p>这是一个段落</p></div>;
export default MyApp;
  • 方式3:React.createClass创建组件的方式在react 16版本中去除,所以不建议使用
var MyApp = React.createClass({
  render: function() {
    return <h1>Hello World!</h1>;
  }
});
(2)使用组件
import React from 'react';
import ReactDOM from 'react-dom';
import MyApp from './js/MyApp.js'; //导入自定义组件

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

2、定义复合组件

我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离

import React from 'react';
import ReactDOM from 'react-dom';

class WebSite extends React.Component {
    render() {
        return (
            <div>
                <Name name={this.props.name} />
                <Link site={this.props.site} />
            </div>
        );
    }
}
class Name extends React.Component {
    render() {
        return (
            <h1>{this.props.name}</h1>
        );
    }
}
class Link extends React.Component {
    render() {
        return (
            <h1>{this.props.site}</h1>
        );
    }
}

ReactDOM.render(
    <WebSite name="百度一下,你就知道" site='http://www.baidu.com' />, 
    document.getElementById('root')
)

React Props

React中的每一个组件,都包含有一个属性(props),属性主要是从父组件传递给子组件的,在组件内部,我们可以通过this.props获取属性对象。

1、定义和使用props

(1)在render方法中调用组件时使用key/value的形式来指定属性。

(2)在自定义组件中通过this.props.key来获得组件属性的值,需要使用{}括起来。

class MyApp extends React.Component {
    render() {
        return (<p>{this.props.name}</p>);
    }
}

ReactDOM.render(
    <MyApp name="张三" />, 
    document.getElementById('root')
);

2、默认Props

  • 在react中可以定义默认props,使用es5时,可以使用getDefaultProps:
var MyApp = React.createClass({
  getDefaultProps: function() {
    return {
      name: 'demo'
    };
  },
  render: function() {
    return <h1>This is my {this.props.name}</h1>;
  }
});

ReactDOM.render(
  <MyApp />,
  document.getElementById('demo')
);
  • es6中使用static defaultProps
class MyApp extends React.Component {
    //如果babel设置为es6的转码方式,会报错,因为定义静态属性不属于es6,而在es7的草案中。ES6的class中只有静态方法,没有静态属性。
    static defaultProps = {
        name: 'demo'
    }
    render() {
        return <p>this is my {this.props.name}</p>
    }   
}

ReactDOM.render(
    <MyApp />,
    document.getElementById('root')
);
  • es6定义默认props的正确方式
class MyApp extends React.Component {
    render() {
        return <p>this is my {this.props.name}</p>
    }   
}
//由于是用ES6 class语法创建组件,其内部只允许定义方法,而不能定义属性,class的属性只能定义在class之外。所以defaultProps要写在组件外部。
MyApp.defaultProps = {
    name: 'xxx'
};

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

3、使用JSX延展属性为组件指定属性

(1)定义一个this.props对象,在对象中声明多个键值对,用于表示组件的属性

(2)在组件中使用{...this.props}的方式传递属性。“...”表示JSX的延展操作符,这种方式可以很方便的为组件指定多个属性。

class MyApp extends React.Component {
    render() {
        return(
            <h1>{this.props.name} : {this.props.age} : {this.props.sex}</h1>
        );
    }
}
MyApp.props = {
    name: '张三', 
    age: 18,
    sex: '男'
};

ReactDOM.render(
    //<MyApp name='张三' age='18' sex='男' />
    <MyApp {...MyApp.props}/>,
    document.getElementById('root')
);

4、Props验证

Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。

var title = 'abc';
// var title = 123;
class MyApp extends React.Component {
    propTypes: {
        title: React.PropTypes.string.isRequired
    }
    render() {
        return (<h1>{this.props.title}</h1>);
    }
}

ReactDOM.render(
    <MyApp title={title}/>, 
    document.getElementById('root')
);

从 React v15.5 开始 ,React.PropTypes 助手函数已被弃用,我们建议使用 prop-types库来定义PropTypes。

import PropTypes from 'prop-types';

class MyApp extends React.Component {
    render() {
        return (
            <h1>{this.props.name}</h1>
        );
    }
}
MyApp.propTypes = {
    name: PropTypes.string
}

ReactDOM.render(
    <MyApp name='123'/>,
    document.getElementById('root')
)

更多的验证器如下:

MyApp.propTypes = {
    // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
    optionalArray: PropTypes.array,
    optionalBool: PropTypes.bool,
    optionalFunc: PropTypes.func,
    optionalNumber: PropTypes.number,
    optionalObject: PropTypes.object,
    optionalString: PropTypes.string,
 
    // 可以被渲染的对象 numbers, strings, elements 或 array
    optionalNode: PropTypes.node,
 
    //  React 元素
    optionalElement: PropTypes.element,
 
    // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
    optionalMessage: PropTypes.instanceOf(Message),
 
    // 用 enum 来限制 prop 只接受指定的值。
    optionalEnum: PropTypes.oneOf(['News', 'Photos']),
 
    // 可以是多个对象类型中的一个
    optionalUnion: PropTypes.oneOfType([
      PropTypes.string,
      PropTypes.number,
      PropTypes.instanceOf(Message)
    ]),
 
    // 指定类型组成的数组
    optionalArrayOf: PropTypes.arrayOf(PropTypes.number),
 
    // 指定类型的属性构成的对象
    optionalObjectOf: PropTypes.objectOf(PropTypes.number),
 
    // 特定 shape 参数的对象
    optionalObjectWithShape: PropTypes.shape({
      color: PropTypes.string,
      fontSize: PropTypes.number
    }),
 
    // 任意类型加上 `isRequired` 来使 prop 不可空。
    requiredFunc: PropTypes.func.isRequired,
 
    // 不可空的任意类型
    requiredAny: PropTypes.any.isRequired,
 
    // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
    customProp: function(props, propName, componentName) {
      if (!/matchme/.test(props[propName])) {
        return new Error('Validation failed!');
      }
    } 
}

React State

React 的核心思想是组件化的思想,应用由组件搭建而成,而组件中最重要的概念是State(状态),State是一个组件的UI数据模型,是组件渲染时的数据依据。

1、如何定义State

定义一个合适的State,是正确创建组件的第一步。State必须能代表一个组件UI呈现的完整状态集,即组件的任何UI改变,都可以从State的变化中反映出来;同时,State还必须是代表一个组件UI呈现的最小状态集,即State中的所有状态都是用于反映组件UI的变化,没有任何多余的状态,也不需要通过其他状态计算而来的中间状态。

组件中用到的一个变量是不是应该作为组件State,可以通过下面的4条依据进行判断:

  1. 这个变量是否是通过Props从父组件中获取?如果是,那么它不是一个状态。
  2. 这个变量是否在组件的整个生命周期中都保持不变?如果是,那么它不是一个状态。
  3. 这个变量是否可以通过其他状态(State)或者属性(Props)计算得到?如果是,那么它不是一个状态。
  4. 这个变量是否在组件的render方法中使用?如果不是,那么它不是一个状态。这种情况下,这个变量更适合定义为组件的一个普通属性。

请务必牢记,并不是组件中用到的所有变量都是组件的状态!当存在多个组件共同依赖一个状态时,一般的做法是状态上移,将这个状态放到这几个组件的公共父组件中

class LikeButton extends React.Component {
    //声明一个状态liked   使用this.state.liked获取状态的值
    getInitialState() {
        return {liked: false};
    }   
    handleClick(event) {
        this.setState({liked: !this.state.liked});
    }
    render() {
        return (
            <button onClick={this.handleClick}>
                你<strong>{this.state.liked ? '喜欢' : '不喜欢'}</strong>我,点我切换
            </button>
        )
    }
}

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

注意:React在ES6的实现中去掉了getInitialState这个hook函数,规定state在constructor中实现

正确定义State的方式如下:

(1)在constructor中实现state

(2)通过bind绑定函数

(3)在函数内部使用setState函数更改状态

(4)在组件中使用该状态

//定义组件
class LikeButton extends React.Component {
    //constructor表示构造器,在constructor需要声明状态state,在声明state之前需要使用super(props);
    constructor(props) {
        super(props);//使用父类的属性
        
        //声明状态
        this.state={
            liked: false
        }
        //Currently, you are calling bind. 
        //But bind returns a bound function. 
        //You need to set the function to its bound value.
        //目前,你正在调用绑定。但是绑定返回绑定函数。您需要将函数设置为其绑定值。
        this.handleClick = this.handleClick.bind(this);
    }
    
    handleClick(event) {
        this.setState({liked: !this.state.liked});
    }
    
    render() {
        return (
            <button onClick={this.handleClick}>
                你<strong>{this.state.liked ? '喜欢' : '不喜欢'}</strong>我,点我切换
            </button>
        )
    }
}

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

2、setState设置状态

(1)语法: setState(object nextState[, function callback])

(2)说明:

  • setState是React事件处理函数中和回调函数中触发UI更新的主要方法。
  • 不能再组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。
  • setState()不一定是同步的,为了性能提升,React会批量执行state和DOM渲染。
  • setState()总是会触发一次组件重绘,但可在shouldComponentUpdate()中实现一些条件渲染逻辑来解决。
class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            clickCount: 0
        };

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

    handleClick(event) {
        this.setState({clickCount: this.state.clickCount + 1});
    }

    render() {
        return (
            <h2 onClick={this.handleClick}>
                点击后次数变更: {this.state.clickCount}
            </h2>
        );
    }
}

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

设置多个状态时,需要在setState函数的第二个参数传递一个function回调函数,如下:

class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            clickCount: 0, 
            isRed: false,
            smallFont: true
        };

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

    handleClick(event) {
        this.setState(
            {clickCount: this.state.clickCount + 1},
            function() {
                this.setState(
                    {isRed: !this.state.isRed}, 
                    function() {
                        this.setState({smallFont: !this.state.smallFont});
                    }
                );
            }       
        );
        console.log(this.state.isred);
    }

    render() {
        var redStyle = {color: 'red', fontSize: 50};
        var blueStyle = {color: 'blue', fontSize: 14};
        return (
            <h2 onClick={this.handleClick} style={this.state.isRed ? redStyle : blueStyle}>
                点击后次数变更: {this.state.clickCount}
            </h2>
        );
    }
}

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

3、State 与 Props 区别

除了State, 组件的Props也是和组件的UI有关的。他们之间的主要区别是:

  • State是可变的,通过setState()函数来改变,是组件内部维护的一组用于反映组件UI变化的状态集合;
  • Props对于使用它的组件来说,是只读的,要想修改Props,只能通过该组件的父组件修改。在组件状态上移的场景中,父组件正是通过子组件的Props, 传递给子组件其所需要的状态。
  • 通过属性将父组件的状态传递到子组件,那么父组件的状态发生变化时,子组件的属性也会改变
  • State定义在constructor内部,在super(props)代码后面;Props定义在类(组件)的外部

React组件生命周期

1、组件的生命周期的三个状态

  • Mounting:已插入真实 DOM
  • Updating:正在被重新渲染
  • Unmounting:已移出真实 DOM

2、生命周期的方法

  • componentWillMount :在渲染前调用,在客户端也在服务端。
  • componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异部操作阻塞UI)。
  • componentWillReceiveProps :在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
  • shouldComponentUpdate :返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。 可以在你确认不需要更新组件时使用。
  • componentWillUpdate:在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
  • componentDidUpdate :在组件完成更新后立即调用。在初始化时不会被调用。
  • componentWillUnmount: 在组件从 DOM 中移除的时候立刻被调用。
class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            date: new Date()
        };
    }
    //通过componentDidMount 方法设置一个定时器,每隔1秒重新设置时间,并重新渲染:
    componentDidMount() {
        var oThis=this;
        clearInterval(this.timer);

        this.timer=setInterval(function() {
            oThis.setState({
                date: new Date()
            })
        }, 1000)
    }
    
    render(){
        return (
            <h2>{this.state.date.toLocaleTimeString()}</h2>
        );
    }
}

父组件的状态传递到子组件的属性中

class Content extends React.Component {
    //在渲染前调用,在客户端也在服务端
    componentWillMount() {
        console.log('Component WILL MOUNT!')
    }
    //在第一次渲染后调用,只在客户端
    componentDidMount() {
       console.log('Component DID MOUNT!')
    }
    //在组件接收到一个新的 prop (更新后)时被调用
    componentWillReceiveProps(newProps) {
        console.log('Component WILL RECEIVE PROPS!')
    }
    //在组件接收到新的props或者state时被调用
    shouldComponentUpdate(newProps, newState) {
        return true;
    }
    //在组件接收到新的props或者state但还没有render时被调用
    componentWillUpdate(nextProps, nextState) {
        console.log('Component WILL UPDATE!');
    }
    //在组件完成更新后立即调用
    componentDidUpdate(prevProps, prevState) {
        console.log('Component DID UPDATE!')
    }
    //在组件从 DOM 中移除的时候立刻被调用
    componentWillUnmount() {
         console.log('Component WILL UNMOUNT!')
    }
  
    render() {
        return (
            <div>
                <h3>{this.props.myNumber}</h3>
            </div>
        );
    }
}

class MyApp extends React.Component {
    constructor(props) {
        super(props);
        //声明状态
        this.state = {
            data: 0
        };
        
        this.setNewNumber = this.setNewNumber.bind(this);
    }
    
    setNewNumber() {
        this.setState({data: this.state.data + 1});
    }
    
    render() {
        return (
            <div>
                <button onClick={this.setNewNumber}>点我改变</button>
                <Content myNumber={this.state.data}></Content>
            </div>
        );
    }
}

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

React表单与事件

1、React表单组件

在HTML中,表单元素与其他元素最大的不同是它自带值或数据,而且在我们的应用中,只要是有表单出现的地方,就会有用户输入,就会有表单事件触发,就会涉及的数据处理。

在我们用React开发应用时,为了更好地管理应用中的数据,响应用户的输入,编写组件的时候呢,我们就会运用到受控组件非受控组件这两个概念。

React推荐我们在绝大多数情况下都使用受控组件。这样可以保证表单的数据在组件的state管理之下,而不是各自独立保有各自的数据。

(1)React中的表单组件大致可分为两类:
  • 受控组件:一般涉及到表单元素时我们才会使用这种分类方法。受控组件的值由props或state传入,用户在元素上交互或输入内容会引起应用state的改变。在state改变之后重新渲染组件,我们才能在页面中看到元素中值的变化,假如组件没有绑定事件处理函数改变state,用户的输入是不会起到任何效果的,这也就是“受控”的含义所在。
  • 非受控组件:类似于传统的DOM表单控件,用户输入不会直接引起应用state的变化,我们也不会直接为非受控组件传入值。想要获取非受控组件,我们需要使用一个特殊的ref属性,同样也可以使用defaultValue属性来为其指定一次性的默认值。

简单来说,受控组件就是它的值由React进行管理,通过props或state来绑定数据,而非受控组件的值则由原生DOM进行管理。示例如下:

//受控组件(省略部分代码)
<input type='text' value={this.state.value} onChange={this.handleChange}/>

handleChange(event) {
    this.setState({value: event.target.value});
}


//非受控组件
<input type="text" defaultValue="hello!"/>
(2)表单组件

我们在组件中声明表单元素时,一般都要为表单元素传入应用状态中的值,可以通过state也可以通过props传递,之后需要为其绑定相关事件,例如表单提交,输入改变等。在相关事件触发的处理函数中,我们需要根据表单元素中用户的输入,对应用数据进行相应的操作和改变。

class MyApp extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 'hello'
        };
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({value: event.target.value});
    }

    render() {
        return (
            <div>
                <input type='text' value={this.state.value} onChange={this.handleChange}/>
                <h2>{this.state.value}</h2>
            </div>
        );
    }
}

ReactDOM.render(
    <MyApp />,
    document.getElementById('root')
);
(3)在子组件上使用表单:更新prop
//子组件
class Son extends React.Component {
 render() {
    return(
        <div>
            <input type='text' value={this.props.myData} onChange={this.props.updateState}/>
            <h2>{this.props.myData}</h2>
        </div>
    );
 }
}

//父组件
class Father extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            data: 'hello'
        };
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({data: event.target.value});
    }

    render() {
        return (
            <div>
                <Son myData={this.state.data} updateState={this.handleChange}/>
            </div>
        );
    }
}

2、React事件

React对原生的事件系统也进行了封装,在React中的事件对象实际上是一个跨浏览器的虚拟事件对象 ,它拥有和原生事件对象相同的属性和方法。

使用React元素处理事件与处理DOM元素上的事件非常相似。不过有一些语法上的差异:

  • React事件使用小驼峰命名法(从第二个单词开始每个首字母大写)。自定义组件时使用大驼峰命名法(每个单词首字母大写)
  • React事件使用JSX传递一个函数作为事件处理程序,而不是一个字符串。

示例:

//HTML
<button onclick="changeData()"> 点击改变 </button>

//React
<button onClick={changeData}> 点击改变 </button>
(1)事件处理函数
  • 鼠标事件:onClick onDoubleClick onMouseDown
  • 触摸事件:onTouchStart onTouchMove onTouchEnd
  • 键盘事件:onKeyDown
  • 剪切事件:onCopy onCut onPaste
  • 表单事件:onChange onInput onSubmit
  • 焦点事件:onFocus
  • UI事件:onScroll
  • 滚动事件:onWheel
(2)onClick事件来修改数据
class ClickEvent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 'hello world'
        };
        this.changeData = this.changeData.bind(this);
    }

    changeData(event) {
        this.setState({value: '萨瓦迪卡'});
    }

    render() {
        return (
            <div>
                <button onClick={this.changeData}>点击改变</button>
                <h2>{this.state.value}</h2>
            </div>
        );
    }
}
(3)在子组件上响应事件:更新prop
//子组件
class Site extends React.Component {
    render() {
        return (
            <div>
                <button onClick={this.props.updateState}>点击改变</button>
                <h2>{this.props.myData}</h2>
            </div>
        );
    }
}

//父组件
class Content extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            value: 'hello'
        };
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) {
        this.setState({value: '你好'});
    }

    render() {
        return(
            <div>
                <Site myData={this.state.value} updateState={this.handleChange}/>
            </div>
        );
    }
}

3、React Refs

组件并不是真实的DOM节点,而是存在于内存之中的一种数据结构,叫做虚拟DOM(virtual DOM)。只有当组件插入文档之后,才会变成真实的DOM。根据React的设计,所有的DOM变动,都先在虚拟DOM上发生,然后再将实际发生变动的部分反映在真实DOM上,这种算法叫做DOM diff,它可以极大提高网页的性能表现。

当需要从组件获取真实DOM的节点时,就需要用到一种非常特殊的属性Ref,可以用来绑定到render()输出的任何组件上。这个特殊的属性允许开发者引用render()返回的相应的支撑实例(backing instance),这样就可以确保在任何时间总是拿到正确的实例。 使用this.refs.[refName]来获取节点

组件支撑实例是React在其文档的各处可引用的真实DOM节点。

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

        this.getFocus = this.getFocus.bind(this);
    }

    getFocus() {
      // 使用原生的DOM API获取焦点
        this.refs.myInput.focus();
    }

    render() {
      // 当组件插入到DOM后,ref属性添加一个组件的引用到`this.refs`
        return(
            <div>
                <input type='text' ref='myInput' />
                <button onClick={this.getFocus}>点击按钮获取焦点</button>
            </div>
        );
    }
}

注意:ref从一定程度上增加了组件之间的耦合性,导致难以分离,所以如果可以用props来处理的事情,尽量不要用ref来处理

值得在 2018 年尝试的 11 个 React 组件库:https://blog.csdn.net/snsHL9db69ccu1aIKl9r/article/details/79454984

React组件库:https://blog.csdn.net/mjzhang1993/article/details/56666607

React全家桶:https://blog.csdn.net/awaw00/article/category/6692955

组件库SaltUI: https://github.com/salt-ui/saltui

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

推荐阅读更多精彩内容

  • HTML模版 之后出现的React代码嵌套入模版中。 1. Hello world 这段代码将一个一级标题插入到指...
    ryanho84阅读 6,137评论 0 9
  • 3. JSX JSX是对JavaScript语言的一个扩展语法, 用于生产React“元素”,建议在描述UI的时候...
    pixels阅读 2,758评论 0 24
  • 原教程内容详见精益 React 学习指南,这只是我在学习过程中的一些阅读笔记,个人觉得该教程讲解深入浅出,比目前大...
    leonaxiong阅读 2,770评论 1 18
  • 1、什么是react React.js 是一个帮助你构建页面 UI 的库。React.js 将帮助我们将界面分成了...
    谷子多阅读 2,520评论 1 13
  • 媳妇表弟叫王攀云,个不高,中等身材。为了媳妇交代的任务,我找了个借口带他出门,路过顺便洗下车。当然不能直说是去补胆...
    李夏辉阅读 398评论 0 1