1.React JSX

React 使用 JSX 来替代常规的 JavaScript。JSX 是一个看起来很像 XML 的 JavaScript 语法扩展。

  1. const element = <h1>Hello, world!</h1>;


1.1将元素渲染到 DOM 中

首先我们在一个 HTML 页面中添加一个 id=”example”

:
在此 div 中的所有内容都将由 React DOM 来管理,所以我们将其称之为 “根” DOM 节点。
要将React元素渲染到根DOM节点中,我们通过把它们都传递给 ReactDOM.render() 的方法来将其渲染到页面上:

  1. var myDivElement = <div className="foo" />;
  2. ReactDOM.render(
  3. myDivElement,
  4. document.getElementById('example')
  5. );

1.2 JavaScript 表达式

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

  1. ReactDOM.render(
  2. <div>
  3. <h1>{1+1}</h1>
  4. </div>
  5. ,
  6. document.getElementById('example')
  7. );

在 JSX 中不能使用 if else 语句,但可以使用 conditional (**三元运算) 表达式来替代。以下实例中如果变量 i 等于 1 浏览器将输出 true, 如果修改 i 的值,则会输出 false。**

  1. ReactDOM.render(
  2. <div>
  3. <h1>{i == 1 ? 'True!' : 'False'}</h1>
  4. </div>
  5. ,
  6. document.getElementById('example')
  7. );

1.3 注释

注释需要写在花括号中,实例如下:

  1. ReactDOM.render(
  2. <div>
  3. <h1>用友春训</h1>
  4. {/*注释...*/}
  5. </div>,
  6. document.getElementById('example')
  7. );

1.4 数组

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

  1. var arr = [
  2. <h1>用友春训</h1>,
  3. <h2>学的不仅是技术,更是梦想!</h2>,
  4. ];
  5. ReactDOM.render(
  6. <div>{arr}</div>,
  7. document.getElementById('example')
  8. );

2.React 组件

本章节我们将讨论如何使用组件使得我们的应用更容易来管理。
接下来我们封装一个输出 “Hello World!” 的组件,组件名为 HelloMessage:

  1. function HelloMessage(props) {
  2. return <h1>Hello World!</h1>;
  3. }
  4. const element = <HelloMessage />;
  5. ReactDOM.render(
  6. element,
  7. document.getElementById('example')
  8. );

注意,原生 HTML 元素名以小写字母开头,而自定义的 React 类名以大写字母开头,比如 HelloMessage 不能写成 helloMessage。除此之外还需要注意组件类只能包含一个顶层标签,否则也会报错。

2.1组件定义

我们可以使用函数定义了一个组件

  1. function HelloMessage(props) {
  2. const {msg} = props;
  3. return <h1>{msg} </h1>;
  4. }
  5. 你也可以使用 ES6 class 来定义一个组件:
  6. class HelloMessage extends React.Component {
  7. render() {
  8. const {msg} = this.props;
  9. return <h1>{msg}</h1>;
  10. }
  11. }

2.2 组件使用

  1. const element = <HelloMessage msg=" Hello World!"/>;
  2. ReactDOM.render(
  3. element,
  4. document.getElementById('example')
  5. );

2.3 复合组件

我们可以通过创建多个组件来合成一个组件,即把组件的不同功能点进行分离。以下实例我们实现了输出网站名字和网址的组件:

  1. function Name(props) {
  2. return <h1>姓名:{props.name}</h1>;
  3. }
  4. function Gender(props) {
  5. return <h1>性别:{props.gender}</h1>;
  6. }
  7. function Age(props) {
  8. return <h1>年龄:{props.age}</h1>;
  9. }
  10. function App() {
  11. return (
  12. <div>
  13. <Name name="小明" />
  14. <Gender gender="男 " />
  15. <Age age="18" />
  16. </div>
  17. );
  18. }
  19. ReactDOM.render(
  20. <App />,
  21. document.getElementById('example')
  22. );

3.React State(状态 )

React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 里,只需更新组件的 state,然后根据新的 state 重新渲染用户界面(不要操作 DOM)。
以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。

  1. class Clock extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {date: new Date()};
  5. }
  6. render() {
  7. return (
  8. <div>
  9. <h1>Hello, world!</h1>
  10. <h2>现在是 {this.state.date.toLocaleTimeString()}.</h2>
  11. </div>
  12. );
  13. }
  14. }
  15. ReactDOM.render(
  16. <Clock />,
  17. document.getElementById('example')
  18. );

3.1 数据自顶向下流动

父组件或子组件都不能知道某个组件是有状态还是无状态,并且它们不应该关心某组件是被定义为一个函数还是一个类。
这就是为什么状态通常被称为局部或封装。 除了拥有并设置它的组件外,其它组件不可访问。
这通常被称为自顶向下或单向数据流。 任何状态始终由某些特定组件所有,并且从该状态导出的任何数据或 UI 只能影响树中下方的组件。
如果你想象一个组件树作为属性的瀑布,每个组件的状态就像一个额外的水源,它连接在一个任意点,但也流下来。
为了表明所有组件都是真正隔离的,我们可以创建一个 App 组件,它渲染三个Clock:

  1. function FormattedDate(props) {
  2. return <h2>现在是 {props.date.toLocaleTimeString()}.</h2>;
  3. }
  4. class Clock extends React.Component {
  5. constructor(props) {
  6. super(props);
  7. this.state = {date: new Date()};
  8. }
  9. componentDidMount() {
  10. this.timerID = setInterval(
  11. () => this.tick(),
  12. 1000
  13. );
  14. }
  15. componentWillUnmount() {
  16. clearInterval(this.timerID);
  17. }
  18. tick() {
  19. this.setState({
  20. date: new Date()
  21. });
  22. }
  23. render() {
  24. return (
  25. <div>
  26. <h1>Hello, world!</h1>
  27. <FormattedDate date={this.state.date} />
  28. </div>
  29. );
  30. }
  31. }
  32. function App() {
  33. return (
  34. <div>
  35. <Clock />
  36. <Clock />
  37. <Clock />
  38. </div>
  39. );
  40. }
  41. ReactDOM.render(<App />, document.getElementById('example'));

以上实例中 FormattedDate 组件将在其属性中接收到 date 值,并且不知道它是来自 Clock 状态、还是来自 Clock 的属性、亦或手工输入。
每个 Clock 组件都建立了自己的定时器并且独立更新。
在 React 应用程序中,组件是有状态还是无状态被认为是可能随时间而变化的组件的实现细节。
我们可以在有状态组件中使用无状态组件,也可以在无状态组件中使用有状态组件。

3.2 更新state

  1. 组件中为我们提供了更新state的方法

setState

  1. setState(object nextState[, function callback])

参数说明:
1. nextState,将要设置的新状态,该状态会和当前的state合并
2. callback,可选参数,回调函数。该函数会在setState设置成功,且组件重新渲染后调用。合并nextState和当前state,并重新渲染组件。setState是React事件处理函数中和请求回调函数中触发UI更新的主要方法。

不能在组件内部通过this.state修改状态,因为该状态会在调用setState()后被替换。
setState()并不会立即改变this.state,而是创建一个即将处理的state。setState()并不一定是同步的,为了提升性能React会批量执行state和DOM渲染。
setState()总是会触发一次组件重绘,除非在shouldComponentUpdate()中实现了一些条件渲染逻辑

  1. class Counter extends React.Component{
  2. constructor(props) {
  3. super(props);
  4. this.state = {clickCount: 0};
  5. this.handleClick = this.handleClick.bind(this);
  6. }
  7. handleClick() {
  8. this.setState(function(state) {
  9. return {clickCount: state.clickCount + 1};
  10. });
  11. }
  12. render () {
  13. return (<h2 onClick={this.handleClick}>点我!点击次数为: {this.state.clickCount}</h2>);
  14. }
  15. }
  16. ReactDOM.render(
  17. <Counter />,
  18. document.getElementById('example')

实例中通过点击 h2 标签来使得点击计数器加 1。

4.React Props

state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。

4.1 使用 Props

以下实例演示了如何在组件中使用 props:

  1. function HelloMessage(props) {
  2. return <h1>Hello {props.name}!</h1>;
  3. }
  4. const element = <HelloMessage name="Runoob"/>;
  5. ReactDOM.render(
  6. element,
  7. document.getElementById('example')
  8. );

实例中 name 属性通过 props.name 来获取。

4.2 默认 Props

你可以通过组件类的 defaultProps 属性为 props 设置默认值,实例如下:

  1. class HelloMessage extends React.Component {
  2. render() {
  3. return (
  4. <h1>Hello, {this.props.name}</h1>
  5. );
  6. }
  7. }
  8. HelloMessage.defaultProps = {
  9. name: 'Runoob'
  10. };
  11. const element = <HelloMessage/>;
  12. ReactDOM.render(
  13. element,
  14. document.getElementById('example')
  15. );

4.3 State 和 Props

以下实例演示了如何在应用中组合使用 state 和 props 。我们可以在父组件中设置 state, 并通过在子组件上使用 props 将其传递到子组件上。在 render 函数中, 我们设置 name 和 site 来获取父组件传递过来的数据。

  1. class WebSite extends React.Component {
  2. constructor() {
  3. super();
  4. this.state = {
  5. name: "菜鸟教程",
  6. site: "https://www.runoob.com"
  7. }
  8. }
  9. render() {
  10. return (
  11. <div>
  12. <Name name={this.state.name} />
  13. <Link site={this.state.site} />
  14. </div>
  15. );
  16. }
  17. }
  18. class Name extends React.Component {
  19. render() {
  20. return (
  21. <h1>{this.props.name}</h1>
  22. );
  23. }
  24. }
  25. class Link extends React.Component {
  26. render() {
  27. return (
  28. <a href={this.props.site}>
  29. {this.props.site}
  30. </a>
  31. );
  32. }
  33. }
  34. ReactDOM.render(
  35. <WebSite />,
  36. document.getElementById('example')
  37. );

4.3 Props 验证

Props 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。
以下实例创建一个 Mytitle 组件,属性 title 是必须的且是字符串,非字符串类型会自动转换为字符串 :

  1. var title = "用友春训";
  2. class MyTitle extends React.Component {
  3. render() {
  4. return (
  5. <h1>Hello, {this.props.title}</h1>
  6. );
  7. }
  8. }
  9. MyTitle.propTypes = {
  10. title: PropTypes.string
  11. };
  12. ReactDOM.render(
  13. <MyTitle title={title} />,
  14. document.getElementById('example')
  15. );

更多验证器说明如下:

  1. MyComponent.propTypes = {
  2. // 可以声明 prop 为指定的 JS 基本数据类型,默认情况,这些数据是可选的
  3. optionalArray: React.PropTypes.array,
  4. optionalBool: React.PropTypes.bool,
  5. optionalFunc: React.PropTypes.func,
  6. optionalNumber: React.PropTypes.number,
  7. optionalObject: React.PropTypes.object,
  8. optionalString: React.PropTypes.string,
  9. // 可以被渲染的对象 numbers, strings, elements 或 array
  10. optionalNode: React.PropTypes.node,
  11. // React 元素
  12. optionalElement: React.PropTypes.element,
  13. // 用 JS 的 instanceof 操作符声明 prop 为类的实例。
  14. optionalMessage: React.PropTypes.instanceOf(Message),
  15. // 用 enum 来限制 prop 只接受指定的值。
  16. optionalEnum: React.PropTypes.oneOf(['News', 'Photos']),
  17. // 可以是多个对象类型中的一个
  18. optionalUnion: React.PropTypes.oneOfType([
  19. React.PropTypes.string,
  20. React.PropTypes.number,
  21. React.PropTypes.instanceOf(Message)
  22. ]),
  23. // 指定类型组成的数组
  24. optionalArrayOf: React.PropTypes.arrayOf(React.PropTypes.number),
  25. // 指定类型的属性构成的对象
  26. optionalObjectOf: React.PropTypes.objectOf(React.PropTypes.number),
  27. // 特定 shape 参数的对象
  28. optionalObjectWithShape: React.PropTypes.shape({
  29. color: React.PropTypes.string,
  30. fontSize: React.PropTypes.number
  31. }),
  32. // 任意类型加上 `isRequired` 来使 prop 不可空。
  33. requiredFunc: React.PropTypes.func.isRequired,
  34. // 不可空的任意类型
  35. requiredAny: React.PropTypes.any.isRequired,
  36. // 自定义验证器。如果验证失败需要返回一个 Error 对象。不要直接使用 `console.warn` 或抛异常,因为这样 `oneOfType` 会失效。
  37. customProp: function(props, propName, componentName) {
  38. if (!/matchme/.test(props[propName])) {
  39. return new Error('Validation failed!');
  40. }
  41. }
  42. }
  43. }

5.React 组件生命周期

组件的生命周期可分成三个状态:
·
Mounting:已插入真实 DOM
·
Updating:正在被重新渲染
·
Unmounting:已移出真实 DOM
生命周期的方法有:
·
componentWillMount 在渲染前调用,在客户端也在服务端。
·
componentDidMount : 在第一次渲染后调用,只在客户端。之后组件已经生成了对应的DOM结构,可以通过this.getDOMNode()来进行访问。 如果你想和其他JavaScript框架一起使用,可以在这个方法中调用setTimeout, setInterval或者发送AJAX请求等操作(防止异步操作阻塞UI)。
·
componentWillReceiveProps 在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。
·
shouldComponentUpdate 返回一个布尔值。在组件接收到新的props或者state时被调用。在初始化时或者使用forceUpdate时不被调用。
可以在你确认不需要更新组件时使用。
·
componentWillUpdate在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。
·
componentDidUpdate 在组件完成更新后立即调用。在初始化时不会被调用。
·
componentWillUnmount在组件从 DOM 中移除之前立刻被调用。
这些方法的详细说明,可以参考官方文档
以下实例在 Hello 组件加载以后,通过 componentDidMount 方法设置一个定时器,每隔100毫秒重新设置组件的透明度,并重新渲染:

  1. class Hello extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {opacity: 1.0};
  5. }
  6. componentDidMount() {
  7. this.timer = setInterval(function () {
  8. var opacity = this.state.opacity;
  9. opacity -= .05;
  10. if (opacity < 0.1) {
  11. opacity = 1.0;
  12. }
  13. this.setState({
  14. opacity: opacity
  15. });
  16. }.bind(this), 100);
  17. }
  18. render () {
  19. return (
  20. <div style={{opacity: this.state.opacity}}>
  21. Hello {this.props.name}
  22. </div>
  23. );
  24. }
  25. }

6.React Refs

React 支持一种非常特殊的属性 Ref ,你可以用来绑定到 render() 输出的任何组件上。
这个特殊的属性允许你引用 render() 返回的相应的支撑实例( backing instance )。这样就可以确保在任何时间总是拿到正确的实例。

6.1 使用方法

给ref属性传入function来获取

  1. <input ref={(el) => this. myInput = el;}/>


你可以通过使用 this 来获取当前 React 组件,或使用 ref 来获取组件的引用,实例如下:

  1. class MyComponent extends React.Component {
  2. handleClick = () => {
  3. // 使用原生的 DOM API 获取input 的value值
  4. this.myAgeInput.value();
  5. }
  6. render() {
  7. // 当组件插入到 DOM 后,ref 属性添加一个组件的引用于到 this.refs
  8. return (
  9. <div>
  10. <input type="text" ref={el => this.myAgeInput = el;} />
  11. <input
  12. type="button"
  13. value="点我输入框获取焦点"
  14. onClick={this.handleClick)}
  15. />
  16. </div>
  17. );
  18. }

7.React 事件处理以及表单

React 元素的事件处理和 DOM 元素类似。但是有一点语法上的不同:
l React 事件绑定属性的命名采用驼峰式写法,而不是小写。
l 如果采用 JSX 的语法你需要传入一个函数作为事件处理函数,而不是一个字符串(DOM 元素的写法)

  1. function ActionLink() {
  2. function handleClick(e) {
  3. e.preventDefault();
  4. console.log('链接被点击');
  5. }
  6. return (
  7. <a href="#" onClick={handleClick}>
  8. 点我
  9. </a>
  10. );
  11. }

7.1 事件处理函数中的this

  1. 你必须谨慎对待 JSX 回调函数中的 this,类的方法默认是不会绑定 this 的。如果你忘记绑定 this.handleClick 并把它传入 onClick, 当你调用这个函数的时候 this 的值会是 undefined。<br /> 你可以使用两种方法解决:<br />1.

箭头函数

  1. class LoggingButton extends React.Component {
  2. handleClick = () => {
  3. console.log('this is:', this);
  4. }
  5. render() {
  6. return (
  7. <button onClick={this.handleClick}>
  8. Click me
  9. </button>
  10. );
  11. }
  12. }
  13. 2.bind方法
  14. class LoggingButton extends React.Component {
  15. handleClick {
  16. console.log('this is:', this);
  17. }
  18. render() {
  19. return (
  20. <button onClick={this.handleClick.bind(this)}>
  21. Click me
  22. </button>
  23. );
  24. }
  25. }


7.2 向事件处理程序传递参数

通常我们会为事件处理程序传递额外的参数。例如,若是 id 是你要删除那一行的 id,以下两种方式都可以向事件处理程序传递参数

  1. <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
  2. <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

7.3 表单以及表单事件

  1. HTML

表单元素与 React 中的其他 DOM 元素有所不同,因为表单元素生来就保留一些内部状态。
在 HTML 当中,像 ,