JSX
可以当做是JavaScript对象,可以对其进行赋值或者设置属性
1 | const element = <div tabIndex="0"></div>; |
这种方法符合es6的语法,同样可以调用React.createElement()
的方法,类似构建虚拟dom
1 | const element = React.createElement( |
也可以在JSX当中使用JavaScript
1 | var names = ['Alice', 'Emily', 'Kate']; |
注意里面是使用大括号隔开
渲染元素
react应用会在html当中寻找<div id="root"></div>
的根节点,并在其上搭建react应用,如果要在react嵌入已有的应用中,这样的根节点可以有多个
可以将dom节点和这个元素一个传进
ReactDOM.render()
当中进行渲染
1 | const element = <h1>Hello, world</h1>; |
react元素是不变的,更新页面的方法是创建一个新的元素,并给ReactDOM.render()
渲染并覆盖原有元素,react内部机制会比较这个dom节点和他的孩子当中不相同的地方并更改
1 | function tick() { |
组件
react当中的组件可以是函数式的组件,只要能够接受一个prop
属性,然后返回一个react元素,用户定义的组件的名称的第一个字母需要大写,注意组件返回的模板里面只能有一个子节点
1 | function Welcome(props) { |
上述例子当中,当react看到用户定义的组件,便会将JSX输出传到组件当中(这个组件是一个函数对象),同时成为这个函数对象的props属性,通过大括号引用进去,之后再进行元素的渲染
注意react组件应该是纯函数,即对传进来的props值不能进行修改,纯函数的定义是对传进来的参数不能进行修改,同时传进相同的参数应该返回相同的结果
状态和生命周期
react组件同样可以使用类来定义,其中类有一个特殊的属性:state
,是类的私有成员
将function组件转化成类组件
- 从
React.Component
当中扩展一个类 - 将
function
定义当中的props
改为this.state
- 将
function
函数体复制到类的方法render()
当中 - 在这个类的构造函数当中初始化state的值
- 将props的值传给基构造函数
1 | class Clock extends React.Component { |
代码当中ReactDOM.render
同样是渲染这个组件,但是提日期的值被封装起来
在类当中添加生命周期
如果要完成对组件内部数据的更新,可以在生命周期函数当中定义函数并更改,不违反组件设计的原则
1 | class Clock extends React.Component { |
通过将一个state
对象传入setState
函数当中,来修改视图的显示
注意
- 除了在
constructor
函数当中,不能直接的修改state
的值,必须通过setState
方法,(类似vuex) state
的变化有可能是异步更新的,所以我们在setState
当中传递的对象不能使用原有state
和props
的值
解决方法:
1 | // Correct |
setState
函数接受一个prevState
表示state
更新之前的状态,props
表示视图已经更新之后的值
事件处理
1 | function ActionLink() { |
注意语法
- 要使用
e.preventDefault()
取消浏览器默认的事件(不能通过return false
来取消) - 事件的语法应该采用驼峰式命名,用大括号包括
- 在组件内定义的事件处理函数应该绑定
this
,不然调state
或props
的时候this
是undefined
,使用属性初始化语法或者直接在定义函数的时候绑定this
的值
1 | class LoggingButton extends React.Component { |
条件渲染
react可以根据props
选择性的渲染组件,在if
的判断的选择性的返回相应JSX
1 | class LoginControl extends React.Component { |
上述代码当中,根据当前的状态this.state
来判断渲染的元素,可以用一个变量a
储存元素,后面使用{a}
将其渲染,将其嵌入JSX当中
true && expression
表达式渲染
1 | function Mailbox(props) { |
通过props
的值进行判断,如果是true
的话进行渲染,如果是false
的话就忽略跳过
三目运算符渲染
1 | render() { |
当只有字面值的时候可以不用加括号
不渲染组件
类似v-if
或者ngIf
,通过判断组件的props
值进行渲染,在组件的当中,如果return null
,那么组件将被忽略,不渲染
1 | function WarningBanner(props) { |
但在这种情况下,组件的生命周期函数依旧会被调用,如componentWillUpdate
,componentDidUpdate
。
列表和数组操作
可以批量创建元素并且将他们用大括号包裹在JSX当中,例如ul
和li
1 | const numbers = [1, 2, 3, 4, 5]; |
键值
在组件当中动态创建元素,为了提高插入和删除的效率,会使用键值标注元素
1 | const todoItems = todos.map((todo, index) => |
在结构组件当中使用键值
键值的设置应该是在模板的<listItems\>
而不是在组件定义的<li>
当中
1 | function ListItem(props) { |
键值
key
是隐式传递到组件当中,所以无法在使用props.key
进行引用,可以用一个另外的属性标注,例如id
表单处理
为了使用表单的提交功能,可以使用控制组件
1 | class NameForm extends React.Component { |
注意代码当中,对用户输入的值value
声明为state
,并同事绑定事件处理函数,value
的值一旦发生改变(即用户输入),就会触发函数对state
的值进行修改,这样就不会违背react组件数据不变的性质
select框处理
1 | class FlavorForm extends React.Component { |
通过封装这个选择的组件,设定value
的值来确定显示的数据,对于数据的更改同样通过绑定Onchange
时间来对state
进行更改
多个input
控件
可以通过在input
当中设置name
的属性,之后在handleChange
函数里面根据e.target.name
进行条件判断进行更改
1 | class Reservation extends React.Component { |
状态提升
react当中要实现状态的共享和同步,必须把组件内部的state
状态提升到父组件的状态,并通过props
组件在子组件当中传递,同时props
的修改,可以类似定义value
和onChange
类似的控制组件来实现数据的更新
1 | class TemperatureInput extends React.Component { |
注意代码当中模板里面value
的改变触发了handleChange
函数,这个函数里面再调用父组件props
属性里面改变父组件state
的方法,实现数据的更新
1 | render() { |
父组件当中模板的定义,当用户对输入框当中输入数字
- 组件
TemperatureInput
中的input
的value
更改,触发handleChange
函数 handleChange
函数调用来自父组件props
的方法onTemperatureChange
onTemperatureChange
函数已经在父组件的模板当中被传入不同的处理函数- 假设我们在第一个
TemperatureInput
当中输入数字,那么最终触发父组件定义的handleCelsiusChange
函数 - 函数里面对父组件里面定义的
state
进行修改,由于另一个TemperatureInput
也是绑定了这个state
,然后传递到子组件当中,修改了input
组件的值
组件的内容
props.children
有点类似vue当中的slot
渲染,可以将父组件当中的属性(JSX)渲染在子组件固定的位置
1 | function FancyBorder(props) { |
1 | function WelcomeDialog() { |
这种形式相当于子组件是一个内部可以嵌套的盒子,将子组件嵌在父组件和父组件的内容当中,使用React.Children.map
可以避免对子元素类型的不确定
props.someProperty
通过在父组件当中具体设置某个插槽(即props
的值),使得这个组件更加易于定制
1 | function Dialog(props) { |
子组件可以使用抽象的props
元素,它具体的值将会由父组件传入
软件设计的原则
关于state
应该遵循三个原则去设计各个组件相应的state
的值,达到最小的消耗
- 封装性,不能是父组件传递到子组件的
props
- 随着时间或者用户交互,这个值会产生变化
- 如果其他
props
和state
能够计算出这个值,那么这个数据不能当做state
### 关于单向数据流
react之间父子组件之间通过单向数据流进行值的传递,子组件的值往往是由父组件的state
属性决定,所以通过用户交互更改子组件的值,并不会改变父组件的state
,要使得子组件像父组件通信,应该将setState
方法传递到子组件当中,然后通过事件处理函数调用setstate
函数,就能实现子组件向父组件进行通信
代码分割
对于一个复杂的应用,可以将模块按需加载,动态地引入,注意是一个异步操作
1 | import React, { Component } from 'react'; |
router
1 |
|
- 在组件当中使用
Link
进行导航 - 最后使用
<Route>
进行渲染,并提供相关的组件,支持路由的嵌套
参数设置
<Route path="messages/:id" component={Message} />
当路由进行匹配的时候可以使用this.props.param.id
来引用
路由首页设置
当我们在匹配路由是/
的时候,希望渲染一个组件,与其他组件不是嵌套的父子关系
1 | import { IndexRoute } from 'react-router' |
Dashboard
将会被渲染在App
组件的{this.props.children}
当中
动态设置路由
1 | import React from "react"; |
代码当中使用了asyncComponent
这个函数来动态加载组件,注意操作是异步的