类组件和函数组件
vue中一个构造选项就表示一个组件;React中一个返回的React元素的函数就是组件
React元素(d小写):const div = React.createElement('div',...)
React组件(D大写):const Div = ()=> React.createElement('div',...)
1. 函数组件
function Welcome(props){
return <h1>hello, {props.name}</h1>
}
使用方法:<Welcome name="frank" />
2. 类组件
class Welcome extends React.Component{
constructor(){
super();
this.state = {}
}
render(){
return <h1>hello, {this.props.name}</h1>
}
}
使用方法:<Welcome name="frank" />
Babel 翻译xml为js的原则
<div />会被翻译为React.craeteElement('div')
<Welcome />会被翻译为React.createElement(Welcome)
React.createElememt的逻辑
如果传入一个字符串'div',则会创一个div(虚拟DOM)
如果传入一个函数,则会调用该函数,获取其返回值
如果传入一个类,则在类前面加个new(导致执行constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值
props和state
react的props相当于vue的props;react的state相当于vue的data
- 添加props(外部数据):
类组件直接读取属性this.props.xxx
函数组件世界读取参数props.xxx
- 添加state(内部数据):
类组件用this.state读,this.setState写
addN = () => {
this.setState({ n: this.state.n + 1 });
};
<button onClick={this.addN}>n+1</button>
函数组件用useState返回数组,第一项读,第二项写【
x读,y写名字随便】
const [n, setN] = React.useState(0);
<button onClick={() => setN(n + 1)}>+1</button>
setState是异步函数,它会异步改变n,react推荐你创建一个新对象【详情见下‘类组件注意项’】
setN永远不会改变n,他会创建一个新的n
小试牛刀
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son />
</div>
);
}
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0
};
}
add() {
// this.state.n += 1 为什么不行
this.setState({ n: this.state.n + 1 });
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.add()}>+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
类组件注意事项:
为啥add写this.state .n+=1页面无变化? 因为他不像vue一样对数据进行监听劫持并渲染在视图上【其实n已经变了,但UI不会自动更新视图】。react调用setState修改数据才会触发UI更新。
不要像下面这样直接改
我们推荐创建新的对象(数据不可变),但不推荐用这种方法
我们推荐setState(函数)这样的写法
(简单情况这样写)
(复杂情况这样写)
因为setState是异步函数。你把n打印出来就会发现点击+1后先打印0再执行+1操作
函数组件注意事项:
与类组件相同的地方:通过setX(新值)来更新UI
与类组件不同的地方:没有this,一律用参数和变量
复杂State
类组件的setState只会自动合并第一层属性(修改值时只会正常复制第一层所有属性再set要改变的属性)更深层的在则需要用Object.assign // 浅拷贝,不要用或者...操作符
函数组件setX完全不会合并,需要自己手动合并
例子:
class Son extends React.Component {
constructor() {
super();
this.state = {
n: 0,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?不会
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?不会
}
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={() => this.addN()}>n+1</button>
m: {this.state.m}
<button onClick={() => this.addM()}>m+1</button>
<Grandson />
</div>
);
}
}
const Grandson = () => {
const [n, setN] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>+1</button>
</div>
);
};
- Object.assign解决【浅拷贝,不要用】
- ...操作符
不推荐对象这种写法,因为不写...state,m就会被置空:
绑定事件
类组件最推荐的写法:
class Son extends React.Component{
addN= ()=>this.setState({ n: this.state.n + 1 })
render(){
return <button onClick={this.addN}>+1</button>
}
}
其他写法:
函数组件写法:
const [n, setN] = React.useState(0); <button onClick={() => setN(n + 1)}>+1</button>
this + 面试题
react理念:
- 数据不可变 --> setState创建一个新的state而不改变原来的state
- diff算法找虚拟DOM间的不同并局部更新DOM
两种编程模型: