一、React 元素和组件
1、元素 Element
const div = React.createElement("div", null, "xxx") // 等价于如下JSX
const div = <div>xxx</div>
React 元素,通常用小写。
2、组件 Component
const Div = ()=>React.createElement("div", null, "xxx") //等价于如下JSX
const Div = ()=><div>xxx</div>
React 组件,通常用大写,它是一个返回 React 元素的函数。
React 中有两种组件:
- 函数组件
- 类组件
2-1、函数组件
【示例】
function Welcome(props){
return <h1>Hello, {props.word}</h1>
}
使用方法:
<Welcome word="react" />,word 会被传入函数组件作为 props 的属性,即 props.word === "react"
2-2、类组件
【示例】
class Welcome extends React.Component{
render(){
return <h1>Hello, {this.props.word}</h1>
}
}
使用方法:
<Welcome word="react" />
2-3、组件的使用
-
原生 HTML 标签,如
<div />,会被转译为React.createElement("div"),即创建一个 div 元素(虚拟 DOM) -
对于一个组件
Welcome,也可以标签的形式使用,写作<Welcome />,相当于React.createElement(Welcome),这里有两种处理方式:Welcome为函数组件,则调用函数,获取其返回值(即Welcome组件对应的 React 元素)Welcome为类组件,则用new生成一个该类的实例,然后调用实例的render的方法,获取其返回值
二、类组件
1、外部数据 props
组件内用 this.props 接收外部数据
class Son extends React.Component{
render(){
return <div>{this.props.n}</div>
}
}
使用时以组件属性的形式传入:
<Son n="x" /> //传字符串
<Son n={x} /> //传表达式
2、内部数据 state
2-1、初始化
在 constructor 中初始化 state:
class Son extends React.Component{
constructor(){
super()
this.state = {
n:1
}
}
}
2-2、修改
修改 state 时,要用 this.setState 方法,不可直接修改,因为 React 并没有像 Vue 一样监听了 state。
2-2-1、setState 的两种用法
setState 传参有两种方式:对象、函数
class Son extends React.Component{
// ...
add(){
this.state.n += 1 //错误写法,直接修改不会更新视图
this.setState(this.state) //不推荐,不要改旧的state
this.setState( {n: this.state.n + 1} ) //正确写法,传入一个全新的对象
this.setState( state => {return {n: state.n + 1}} ) //更高级的写法,传一个函数
}
}
函数的写法更好,因为 setState 是异步的,用对象的写法容易造成误解:
add(){
this.setState({n: this.state.n + 1})
console.log(this.state.n) //此处本意是希望打出新的n,但实际会打出旧的n
}
用函数的写法可以避免混淆:
add(){
this.setState(state => {
const n = state.n + 1
console.log(n) //此处会打印出新的n
return {n}
})
}
setState 会调用传入的函数 fn ,并且传入旧的 state ,即 fn(this.state)。
【总结】
setState异步更新 UI- 不要改动旧的
state,用setState(/* 全新的对象 */) - 更推荐的写法:
setState(/* 函数 */)
2-2-2、setState 对复杂 state 的处理
如果 state 中有多个属性,例如:
state = {
m:0,
n:0
}
要修改 n,直接 setState( {n: 1} ) 即可,无需带上 m,setState 会自动合并 m,相当于 setState({...state, n: 1})
【注】
只有 state 的第一层属性会自动合并,深层的并不会合并,如:
state = {
m:0,
obj: {
a:0,
b:0
}
}
若 setState( { obj:{a:1} } ),则 b 会被置空。
正确写法:setState( { obj:{...state.obj, a: 1} } )
3、事件绑定
类组件的事件绑定比较复杂,涉及到关于 this 的细节。
假设类组件有 addN 方法:
class Son extends React.Component{
// ...
addN(){
this.setState({n: this.state.n + 1})
}
}
监听点击事件:
<button onClick={this.addN}>n+1</button>
//此为错误写法!这样会使 this.addN 中的 this 为 undefined
//调用形式为 button.onclick.call(undefined, event)
<button onClick={()=>this.addN}>n+1</button>
//正确写法,使用箭头函数
<button onClick={this.addN.bind(this)}>n+1</button>
//正确写法,将 addN 函数的 this 绑定为当前实例,但麻烦!
由于必须使用箭头函数,而类中的方法(原型)不能写成箭头函数的形式,因此可以将事件监听器作为实例的属性,这是最佳写法:
class Son extends React.Component{
addN = ()=>this.setState(/* ... */) //等价于以下写法,是一种语法糖
constructor(){
this.addN = ()=>this.setState(/* ... */)
}
}
绑定事件时可直接用 this.addN
<button onClick={this.addN}>n+1</button>
三、函数组件
1、外部数据 props
组件用 props 参数接收外部数据:
function Son(props){
return <div>{props.n}</div>
}
2、内部数据 state
2-1、初始化
在函数内初始化数据:
function Son(){
const [n, setN] = React.useState(0) //初始化一个变量n=0,setN为修改n的函数
return (
<div>
{n}
<button onClick="{()=>setN(n+1)}">+1</button>
</div>
)
}
2-2、修改
单一数据,用 setN 修改 n,传入新值即可。
复杂 state
- 推荐单独声明各个变量,例如:
const [x, setX] = React.useState(0)
const [y, setY] = React.useState(0)
修改谁,就调用相应的 set 函数即可。
- 如果使用对象,则比较麻烦:
const [state, setState] = React.useState({m:0, n:0})
要改 n,若只写 setState( {n:1} ),并不会自动合并 m, 会导致 state.m 不存在 。
所以必须带上其余的属性,即setState({...state, n:1})