30分钟快速入门React

626 阅读8分钟

React简介

React是用于构建Web和原生交互界面的开源JS库。

React特点

  • 声明式设计,为应用的每一个状态设计简洁的视图,当数据改变的时候,React能有效、正确的更新、渲染组件。

  • 高效:React采用虚拟DOM,极大的提升了UI更新和渲染的效率;

  • 灵活:React允许结合其它框架或者库一起使用;

  • JSX:JSX是JS语法的扩展,能很好的描述UI应该呈现出的应有的交互的本质形式;

  • 单向响应式数据流:React采用了单向响应的数据流,使得组件状态清晰,模块化更易于维护和开发。

小试牛刀

使用create-react-app工具,创建React环境:

import React from 'react'  // 很新库
import ReactDOM from 'react-dom' // 与DOM相关的功能

function SayHello(props) {
    return <h1>say hello to you</h1>
}

// 将SayHello组件插入到id为root的标签中
ReactDOM.render(
    <SayHello />,
    document.getElementById('root')
)

启动项目,打开localhost:3000,即可看到页面上的h1标签样式展示的say hello to you。

React安装

React可以直接下载安装(官网:zh-hans.reactjs.org/)基础环境:Node。

  1. 查看Node版本以及npm,需要node >= 6npm >= 5.2(建议使用cnpm)。
    node -v
    npm -v
  1. 全局安装React:create-react-app,通过该命令,无需配置即可快速构建React环境(基于webpack+ES6).
    npm install create-react-app -g 
    
    // cnpm安装
    npm install -g cnpm --registry=https://registry.npm.taobao.org
    npm config set registry https://registry.npm.taobao.org
    
    npm install create-react-app -g 

  1. 创建项目
    // create-react-app 创建命令
    // my-app 项目名
    create-react-app my-app
    
    // 进入项目
    cd my-app
    npm start

React元素渲染

React元素都是不可变的:当元素被创建后,无法改变其内容和属性。更新办法:创建一个新的元素,将它传入ReactDOM.render()中:

<div id="example"></div>
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>现在是 {this.props.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
 
function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('example')
  );
}
 
setInterval(tick, 1);

运行结果如下:

image.png

注:React 只会更新必要的部分

React DOM 首先会比较元素内容先后的不同,在渲染过程中只会更新改变了的部分。

JSX语法

JSX:类XML的JS语法扩展(语法糖)。元素是构成React应用的最小单位,JSX就是用来声明其中的元素的。

特点

  • 执行快,因为它在编译为JS代码后进行了优化;
  • 类型安全,在编译过程中能快速发现错误;
  • 快速:语义化,更直观,可读性更高;

使用

  • 属性:className替换class,htmlFor替换for属性;
  • JSX创建DOM,所有的节点,必须有唯一的根元素进行包裹;
  • JSX中可直接使用JS代码,通过{}包含即可;
  • JSX中只能出现表达式,不能出现语句;
  • 注释语法:{/* 中间是注释的内容 */}

具体使用参考文章:10分钟快速掌握React中的JSX:

React组件

  1. 使用函数定义组件或者使用ES6的class来定义一个组件(大写字母开头,组件类只能包含一个根节点标签
// 方法定义
function MyComponent({name, age}) {
    return <div>hello, my name is {name} and i am a {age} years old.</div>
}

// class定义
class MyComponent1 extends React.Component {
    render() {
        return <div>hello, my name is {name} and i am a {age} years old.</div>
    }
}

// 使用
const element = <MyComponent name='Jack' age=18 />

React.render(
    element,
    document.getElemementById('root')
)

组件API

  • 状态
    • setState(object nextState[, callback])
      • setState()会合并更新与当前的状态,并不会立即改变this.state,而是创建一个即将处理的statesetState()不一定是同步的,为了提升性能React批量执行state和DOM渲染;
      • setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑;
    • replaceState(object nextState[, callback])
      • replaceState()仅保留nextState中的状态。
  • 属性
    • setProps(object nextProps[, callback])
    • replaceProps(object nextProps[, callback])
  • 强制更新:forceUpdate([callback])
    • callback会在组件render()方法调用后调用
    • 避免使用
  • 获取DOM节点:findDOMNode()
  • 判断组件挂载状态:isMounted():判断组件是否已挂载,保证异步场景下设置状态不出错。

组件的生命周期

  • Mounting(挂载):已插入真实 DOM
    • constructor(): 在 React 组件挂载之前,会调用它的构造函数。
    • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。
    • render(): render() 方法是 class 组件中唯一必须实现的方法。
    • componentDidMount(): 在组件挂载后(插入 DOM 树中)立即调用。
  • Updating(更新):正在被重新渲染
    • getDerivedStateFromProps(): 在调用 render 方法之前调用,并且在初始挂载及后续更新时都会被调用。根据 shouldComponentUpdate() 的返回值,判断 React 组件的输出是否受当前 state 或 props 更改的影响。
    • shouldComponentUpdate():当 props 或 state 发生变化时,shouldComponentUpdate() 会在渲染执行之前被调用。
    • render(): render() 方法是 class 组件中唯一必须实现的方法。
    • getSnapshotBeforeUpdate(): 在最近一次渲染输出(提交到 DOM 节点)之前调用。
    • componentDidUpdate(): 在更新后会被立即调用。
  • Unmounting(卸载):已移出真实 DOM
    • componentWillUnmount(): 在组件卸载及销毁之前直接调用。

React Ajax

React最贱的数据可以通过componentDidMount方法中的Ajax获取,当从服务端获取数据时可以将数据存储在state中,在用this.setState方法重新渲染UI。

当使用异步加载数据时,在组件卸载前使用componentWillUnmount来取消未完成的请求。

class UserGist extends React.Component {
  constructor(props) {
      super(props);
      this.state = {username: '', lastGistUrl: ''};
  }
 
 
  componentDidMount() {
    this.serverRequest = $.get(this.props.source, function (result) {
      var lastGist = result[0];
      this.setState({
        username: lastGist.owner.login,
        lastGistUrl: lastGist.html_url
      });
    }.bind(this));
  }
 
  componentWillUnmount() {
    this.serverRequest.abort();
  }
 
  render() {
    return (
      <div>
        {this.state.username} 用户最新的 Gist 共享地址:
        <a href={this.state.lastGistUrl}>{this.state.lastGistUrl}</a>
      </div>
    );
  }
}
 
ReactDOM.render(
  <UserGist source="https://api.github.com/users/octocat/gists" />,
  document.getElementById('example')
);

小试牛刀

在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染:

class Hello extends React.Component {
 
  constructor(props) {
      super(props);
      this.state = {opacity: 1.0};
  }
 
  componentDidMount() {
    this.timer = setInterval(function () {
      var opacity = this.state.opacity;
      opacity -= .05;
      if (opacity < 0.1) {
        opacity = 1.0;
      }
      this.setState({
        opacity: opacity
      });
    }.bind(this), 100);
  }
 
  render () {
    return (
      <div style={{opacity: this.state.opacity}}>
        Hello {this.props.name}
      </div>
    );
  }
}
 
ReactDOM.render(
  <Hello name="world"/>,
  document.body
);

React表单事件

表单

在React中,可变的状态通常保存在组件的状态属性中,并且只能用setState()进行更新。因此,需要通过一定的事件处理完成表单数据同步更新。

class HelloMessage extends React.Component{
    constructor(props) {
        // 调用父类Component的props的构造方法
        super(props);
        this.state = {name:'LeaT'}
        this.handleChange = this.handleChange.bind(this)
    }
    
    handleChange(event) {
        this.setState({name: event.target.value})
    }
    
    render() {
        let value = this.state.name
        return <div>
            <input value={value} onChange={this.handleChange} type="text" />
            <h4>Say hello to {value}</h4>
        </div>
    }
}

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

事件处理

class HelloMessage extends React.Component {
  constructor(props) {
      super(props);
      this.state = {value: 'Hello Runoob!'};
      this.handleChange = this.handleChange.bind(this);
  }
  
  handleChange(event) {
    this.setState({value: 'hello'})
  }
  render() {
    var value = this.state.value;
    return <div>
            <button onClick={this.handleChange}>点我</button>
            <h4>{value}</h4>
           </div>;
  }
}
ReactDOM.render(
  <HelloMessage />,
  document.getElementById('example')
);

React state(状态)

React状态反应出来其实就是组件是一个状态机。通过与用户的交互实现不同状态修改、更新UI,保持用户界面与数据的一致性。

单向数据流

组件的数据是单向数据流:任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或UI只能影响树中甲方的组件。

小试牛刀

function FormattedDate(props) {
  return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
}
 
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }
 
  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }
 
  componentWillUnmount() {
    clearInterval(this.timerID);
  }
 
  tick() {
    this.setState({
      date: new Date()
    });
  }
 
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <FormattedDate date={this.state.date} />
      </div>
    );
  }
}
 
function App() {
  return (
    <div>
      <Clock />
      <Clock />
      <Clock />
    </div>
  );
}
 
ReactDOM.render(<App />, document.getElementById('example'));

React props

props相对于state是不可变的。通常,子组件就是通过props来传递数据。

class HelloMessage extends React.Component {
  render() {
    return (
      <h1>Hello, {this.props.name}</h1>
    );
  }
}
 
HelloMessage.defaultProps = {
  name: 'Runoob'
};
 
const element = <HelloMessage/>;
 
ReactDOM.render(
  element,
  document.getElementById('example')
);

父子组件参数传递

class WebSite extends React.Component {
  constructor() {
      super();
 
      this.state = {
        name: "菜鸟教程",
        site: "https://www.runoob.com"
      }
    }
  render() {
    return (
      <div>
        <Name name={this.state.name} />
        <Link site={this.state.site} />
      </div>
    );
  }
}
 
 
 
class Name extends React.Component {
  render() {
    return (
      <h1>{this.props.name}</h1>
    );
  }
}
 
class Link extends React.Component {
  render() {
    return (
      <a href={this.props.site}>
        {this.props.site}
      </a>
    );
  }
}
 
ReactDOM.render(
  <WebSite />,
  document.getElementById('example')
);

propTypes(prop验证)

propTypes主要用于保证应用组件的正确使用,验证传参的数据格式和有效性。

验证器说明:

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

React事件处理

  • 驼峰式写法:onClick=
  • JSX语法 {}包含函数名:onClick={activeLasers}
  • 阻止默认行为,需要使用preventDefault,不可使用false的方式阻止默认行为。
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};
 类的方法默认是不会绑定this的。需手动绑定,在回调中才可使用
    this.handleClick = this.handleClick.bind(this);
  }
 
  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }
 
  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}
 
ReactDOM.render(
  <Toggle />,
  document.getElementById('example')
);

React条件渲染

  • &&
  • xx?xxx:xxxx
  • 阻止条件渲染:return null即可,不会影响组件生命周期方法的回调。

React中的列表和key

React中的数组可直接使用,渲染时会平铺每一个元素。

function ListItem(props) { 
    return <li>{props.value}</li>; 
}

使用JSX

function NumberList(props) {
  const numbers = props.numbers;
  const listItems = numbers.map((number) =>
    <ListItem key={number.toString()}
              value={number} />

  );
  return (
    <ul>
      {listItems}
    </ul>
  );
}

function NumberList(props) {
  const numbers = props.numbers;
  return (
    <ul>
      {numbers.map((number) =>
        <ListItem key={number.toString()}
                  value={number} />
 
      )}
    </ul>
  );
}

key

React中的key主要用于DOM更新时,虚拟DOM的diff算法中使用,一个元素的key应该在列表以及兄弟元素中拥有一个独一无二的特性,全局唯一。

React refs

React允许在任何组件和元素上添加ref属性,通过this.refs.xx获取指定xx属性的组件的引用。

class MyComponent extends React.Component {
  handleClick() {
    // 使用原生的 DOM API 获取焦点
    this.refs.myInput.focus();
  }
  render() {
    //  当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
    return (
      <div>
        <input type="text" ref="myInput" />
        <input
          type="button"
          value="点我输入框获取焦点"
          onClick={this.handleClick.bind(this)}
        />
      </div>
    );
  }
}
 
ReactDOM.render(
  <MyComponent />,
  document.getElementById('example')
);