在React中,一个返回React元素的函数就是组件
在Vue中,一个构造选项就可以表示一个组件
const div = React.createElement('div',...)
这是一个React元素 d小写
const Div =()=>React.createElement('div',...)
这是一个React组件 D大写
两种组件
函数组件
function Welcome(props){
return <h1>Hello,{props.name}</h1>
}
//使用方法:<Welcome name='Origami' />
类组件
class Welcome extends React.Component{
render(){
return <h1>Hello,{this.props.name}</h1>
}
}
//使用方法:<Welcome name='Origami' />
class组件需要写一个render函数用于渲染视图。
此处的会被翻译成React.createElement(Welcome)
React.createElement的逻辑
-
如果传入一个字符串'div',则会创建一个div
-
如果传入一个函数,则会调用这个函数,获取它的返回值
-
如果传入一个类,则在这个类前面加一个new(会执行constructor),获取一个组件对象,然后调用对现哥render方法,获取返回值
两种写法的示例
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.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);
Props和State
使用外部数据props
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Son messageForSon="儿子你好" />
</div>
);
}
class Son extends React.Component {
render() {
return (
<div className="Son">
我是儿子,我爸对我说「{this.props.messageForSon}」
<Grandson messageForGrandson="孙子你好" />
</div>
);
}
}
const Grandson = props => {
return (
<div className="Grandson">
我是孙子,我爸对我说「{props.messageForGrandson}」
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
通过在标签中写xxx="yyy"
类组件直接读取属性this.props.xxx即可读取yyy
函数组件直接读取参数props.xxx即可读取yyy
使用内部数据state
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.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);
依然是这一段代码
类组件用this.state读,this.setState写
如写:
add() {
this.setState({ n: this.state.n + 1 });
}
读:儿子 n:{this.state.n}
另外初始化时必须在constructor内写super
这里add()不能用this.state.n+=1是因为虽然n已经改变了,但是UI没有同步更新,需要调用setState才会触发UI的更新,但是这是一个异步的更新,因为React没`有像Vue监听data那样监听state
所以诞生了更好的写法那就是
this.setState((state)=>{
return {n:this.state.n+1}
})
即setState(函数)
此外this.setState(this.state)并不被推荐,因为React并不希望我们直接修改旧对象,更希望生成一个新的对象。比如setState({n:this.state.n+1})
函数组件用useState返回数组,第一项读,第二项写
声明时const [n, setN] = React.useState(0)中useState(0)的0表示初始值
setN(n+1)是写,但是永远不会改变n,而是产生一个新的n
复杂state
总结:
类组件的setState会自动合并第一层属性,但不会合并第二层属性。
使用Object.assign或者...操作符把之前的内容写进来,在修改局部,否则会出现置空现象。
函数组件则不会自动合并任何属性,如果要合并只能通过...操作符
事件绑定
<button onClick={() => this.add()}>+1</button>这样写最为稳定
<button onClick={this.add.bind(this)}>+1</button>这样写返回了一个绑定当前this的心函数,是可以的
<button onClick={this.add}>+1</button>这样会让this.add里面的this变成window,是有问题的
最好的写法
add = () => {
this.setState((state)=>{
return {n:this.state.n+1}
})
};
render() {
return (
<div className="Son">
儿子 n: {this.state.n}
<button onClick={this.add}>n+1</button>
定义时用箭头函数,调用时直接写函数名。
Vue和React在编程模型上的区别
Vue编程
一个对象对应一个虚拟DOM,当对象的属性改变时,把属性相关的DOM节点全部更新
React编程模型
一个对象对应一个虚拟DOM,另一个对象对应两一个虚拟DOM,对比两个虚拟DOM,通过DOM diff找不同实现局部更新UI。