- 类组件和函数组件
- 如何使用props和state
- 如何绑定事件
props对应Vue中的props
state对应Vue中的data
就目前而言,返回React元素的函数就是组件
在Vue里面,一个构造选项就可以表示一个组件
函数组件
function Welcome(props){
return <h1>hello,{props.name}</h1>
}
使用方法:<Welcome name="frank"/>
类组件
class Welcome extends React.Component{
render(){
return <h1>hello,{this.props.name}</h1>
}
}
使用方法和类组件一样
<Welcome/>
会被翻译成React.createElement(Welcome)
React.createElement的逻辑
- 传入一个字符串"div",则创建一个div(虚拟DOM)
- 传入一个函数,则会调用该函数,获取返回值
- 传入一个类,则在类前面加个
new
(这会导致constructor执行),获取一个组件对象,然后调用对象的render方法,获取其返回值
试着去使用两种组件
import React from "react";
import ReactDOM from "react-dom";
function App() {
return (
<div>
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}
add() {
this.setState({n:this.state.n + 1});//()里面是新的对象
}
render(){
return (
<div>
儿子n:{this.state.n}
<button onClick={()=>this.add()}>+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div>
孙子n:{n}
<button onClick={()=>setN(n+1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
setN
是得到新的n
内部数据和外部数据的使用
外部数据props
import React from "react";
import ReactDOM from "react-dom";
function App() {
return (
<div>
爸爸
<Son messageForSon="儿子你好" />
</div>
);
}
class Son extends React.Component {
render() {
return (
<div>
我是儿子,我爸对我说:{this.props.messageForSon}
<Grandson messageForGrandson="孙贼你好" />
</div>
);
}
}
const Grandson = props => {
return (
<div>
我是孙子,我爸对我说:{props.messageForGrandson}
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
内部数据state
import React from "react";
import ReactDOM from "react-dom";
function App() {
return (
<div>
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}//数据初始化
add() {
this.setState({n:this.state.n + 1});
}//写数据,()里面是新对象,要改变数据就要产生新的对象
render() {
return (
<div>
儿子n:{this.state.n}//读数据
<button onClick={()=>this.add()}>+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n,setN] = React.useState(0);
return (
<div>
孙子n:{n}//读数据
<button onClick={()=>setN(n+1)}>+1</button>//setN是写数据
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
类组件中
setState里面最好写函数
add(){
this.setState((state)=> {
return {n:state.n+1}
});
}
因为setState
是异步的,如果后面还有代码使用了this.state.n
,那么就会出问题。
写函数就可以在函数中使用最新的this.state.n
,你就是很清楚新n
和旧n
,避免异步造成的错误
函数组件中
setN
不会改变n
类组件中的setState
会等一会儿改变n
总结
外部数据
- 类组件直接读取属性
this.props.xxx
- 函数组件直接读取参数
props.xxx
内部数据 - 类组件用
this.stat
读,this.setState
写 - 函数组件用
useState
返回数组,第一项读,第二项写
类组件到的注意事项
this.state.n+=1
有用吗?
- n已经变了,没有触发UI更新
- 调用setState才会触发UI更新(异步更新)
- 因为React没有像Vue监听data一样监听state
setState
推荐写法 - setState之后,state不会立马改变,立马读取state会失败
setState(函数)
- React希望我们不要修改旧state(不可变数据)
- 常用代码:setState({n:state.n+1})
函数组件注意事项
- 也要通过setX(新值)来更新UI
- 没有this,一律用参数和变量
Vue和React
- Vue就是监听数据变化,然后更新UI
- React不能修改之前的数据,只能创建一个新的数据,通过diff找到不同,局部更新UI
复杂的state
如果state里面有不止有一个值,有多个的时候
类组件:
this.state = {
n: 0,
m: 0
};
addN(){
this.setState({n: this.state.n + 1});
}
要改变n的值,就像上面这样写就行,但是m呢,上面并没有把没有变化的m赋值过去,
上面的写法可以看成this.setState({...this.state, n: this.state.n+1});
,自动把没有变化的值赋值给新的对象
函数组件:
const Grandson = () => {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
return (
<div>
孙子n:{n}
<button onClick={() => setN(n+1)}>n+1</button>
m:{m}
<button onClick={() => setM(m+1)}>m+1</button>
</div>
);
};
这样改变一个值也不会把另外的变为undefined
下面是不推荐写法,就会把另外的变为undefined
const Grandson = () => {
const [state, setState] = React.useState({
n: 0, m:0
});
return (
<div>
孙子n:{state.n}
<button onClick={() => setState({n:state.n+1})}>n+1</button>
m:{state.m}
<button onClick={() => setState({m:state.m+1})}>m+1</button>
</div>
);
};
点了n+1后m直接没了
<button onClick={() => setState({...state, n:state.n+1})}>n+1</button>
就必须这样写
注意
类组件的setState会自动合并,函数组件的setState不会自动合并,并且只会自动合并第一层。
this.state = {
n: 0,
m: 0,
user: {
name: "frank",
age: 18
}
}
render(){
return (
<div>
<div>user.name: {this.state.user.name}</div>
<div>user.age: {this.state.user.age}</div>
<button onClick={() => this.changeUser()}>change user</div>
</div>
)
}
changeUser(){
this.setState({
user: {
name: "jack"
}
});
}
上面的代码,点击change user
按钮,名字被改变,然后age就没了,因为指挥自动合并第一层。
改成
changeUser(){
this.setState({
user: {
...this.state.user,
name: "jack"
}
});
}
这样把之前的值赋值回来的写法即可
也可以用Object.assign()
const user = Object.assign({}, this.state.user);
上面的写法等价于
const user = {...this.state.user};
Object.assign(参数1,参数2)
就是把参数2的值拷贝给参数1,然后赋值给user
函数组件则是完全不会帮你合并
全都要你自己去合并
事件绑定
有下面的写法
-
<button onClick={()=>this.addN()}>n+1</button>
上面的这种写法最安全 -
<button onClick={this.addN}>n+1</button>
有问题,这样会使的this.addN
中addN
里面的this
变成window
,因为在调用这个函数的时候,是用onClick
调用的
相当于button.onClick.call(null,event)
,this是空,所以this指向window
在addN
函数里面调用this.state
时就会出问题,因为值个this
没有指向这个react实例
,所以会出问题 -
<button onClick={this.addN.bind(this)}>n+1</button>
用bind给this绑定为react实例,可以
下面的写法用this._add = () => this.addN()
<button onClick={this._addN}>n+1</button>
就可以愉快的使用了
上面的写法多多少少有点不够优雅
看下面的
把addN变成了箭头函数
constructor(){
this.addN = ()=>this.setState({n:this.state.n+1})
render(){
return <button onClick={this.addN}>n+1</button>
}
}
就不用在constructor外面写方法了
来看看一看怎么变过去的
最开始是下面的写法
constructor(){
this._addN = ()=> this.addN()
render(){
return <button onClick={this.addN}>n+1</button>
}
}
addN() {
this.setState({n: this.state.n + 1});
}
最终得到了一个终极写法
去掉this
然后把剩下的写在constructor
外面
this.addN = () => this.set....
箭头函数是挂在this对象上面的
addN(){}
和addN: function(){}
都是正常的函数写法,会挂在原型上面
看下面的写法
class Person{
sayHi(){}
}
class Person2{
sayHi2 = ()=>{}
}
两种不同的写法
- 第一种的sayHi在原型上,
- 第二种sayHi在实例本身身上
最好用第二种写法,方法都在自己身上
this
- 所有函数的this都是参数,由调用决定,所以可变
- 箭头函数的this不可变,箭头函数不接受this
React VS Vue
相同点
- 都是对视图的封装,React使用组件和类表示一个组件,Vue是通过构造选项构造一个组件
- 都提供了
createElement
的XML简写,React提供的是JSX语法,Vue提供的是模板写法(语法巨多)
不同点
- React是All in js(HTML in js)
- Vue是js放在HTML里写(js in html)