React 组件
Element V.S. Component
元素与组件
- const div = React.createElement('div',...),这是一个React元素(d小写)
- const Div = () => React.createElement(‘div’,...) 这是一个React组件(D大写)
组件是什么
- 能与其它物件组合起来的 , 就是组件
- 就目前而已,一个返回React 元素的函数就是组件,在Vue里,一个构造选项就可以表示一个组件
React两种组件
一、函数组件
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 name="frank" />
会被翻译成什么
- 会被翻译成 React.createElement('div')
-
翻译为 React.createElement(Welcome) -
可以用 babel online 直接翻译React看看效果
React.createElement 的逻辑
- 如果传入一个字符串 ‘div,则会创建 一个 div
- 如果传入一个函数,则会调用该函数,获取其返回值
- 如果传入一个类,则在类前面加个 new(这会导致执行 constructor),获取一个组件对象,然后调用对象的render方法,获取其返回值
示例1
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(外部数据)
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);
// 类组件直接读取属性 this.props.xxx,函数组件直接读取参数 props.xxx
添加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.state.n += 1 为什么不行
// this.setState({ n: this.state.n + 1 });
this.setState((state) => { //推荐方式
const n = state.n + 1;
console.log(n);
return { n };
});
}
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写,函数组件用useState返回数组,第一项读、第二项写。
类组件注意事项
this.state.n +=1 无效?
- 其实n已经改变了。只不过UI不会自动更新,调用setState才会触发UI更新(异步更新)
- 因为React没有像 Vue 监听data一样监听 state
setState 会异步更新UI
- setState之后,state不会马上改变,立马读state会失败
- 更推荐的方式是setState(函数)
this.setState(this.state) 不推荐
- React希望我们不要修改旧state(不可变数据)
- 常用代码:setState({n:state.n+1})
- React这些是一种函数式理念
函数组件
- 跟类组件类似的地方:也要通过setX(新值)来更新UI
- 跟类组件不同的地方:没有this,一律用参数和变量
两种变成模型
Vue的编程模型
一个对象,对应一个虚拟DOM,当对象的属性改变时,把属性相关的DOM节点全部更新
注:Vue为了其他考量,也引入了虚拟DOM和DOM Diff,上图只是简化
React的编程模型
一个对象,对应一个虚拟DOM。另一个对象,对应另一个虚拟DOM。
对比两个虚拟DOM,找出不同(DOM diff)最后局部更新DOM
复杂 state
如果state里面不止有n怎么办
// 类组件里面有n 和 m
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,
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>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
// 函数组件 里面有n和 m
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,
m: 0
};
}
addN() {
this.setState({ n: this.state.n + 1 });
}
addM() {
this.setState({ m: this.state.m + 1 });
}
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);
const [m, setM] = React.useState(0);
return (
<div className="Grandson">
孙子 n:{n}
<button onClick={() => setN(n + 1)}>n+1</button>
m:{m}
<button onClick={() => setM(m + 1)}>m+1</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
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,
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 [state, setState] = React.useState({
n: 0,
m: 0
});
return (
<div className="Grandson">
孙子 n:{state.n}
<button onClick={() => setState({ ...state, n: state.n + 1 })}> //不加...state m会被变成undefined
n+1
</button>
m:{state.m}
<button onClick={() => setState({ ...state, m: state.m + 1 })}>
m+1
</button>
</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
总结
- 类组件的setState会自动合并第一层属性,但是并不会合并第二层属性,解决方法:使用Object.assign或者...操作符
- 函数组件的setX则完全不会帮忙合并,需要合并是手动写...操作符把之前的复制过来合并。
事件绑定
类组件的事件绑定
<button onClick={()=> this.addN()}>n+1</button>
// 传一个函数给 onClick 即可
// 还有一种写法:
<button onClick={this.addN.bind(this)}>n+1</button>
// 这样写是可以的,因为它返回一个绑定了当前this的新函数,但是这样写太麻烦,还不如第一种
//可以用 this._addN = () => this.addN() ,给箭头函数取个名字,然后写成
<button onClick={this._addN}>n+1</button>
//错误写法:这样写会使this.addN里的this变成window
<button onClick={this.addN}>n+1</button>
// 终极写法
class Son extends React.Component{
addN = () => this.setState({n: this.state.n + 1})
constructor(){
this.addN = () => this.setState({n:this.state.n+1})
}
// 上面两种写法完全等价
//下面两种写法完全等价
addN(){this.setState({n.this.state.n+1})
addN:function(){
this.setState({n:this.state.n+1})
}
}
这四种addN有什么区别?
-
上面两种在对象上,下面两种在原型上,下面的addN的this可变成window,上面的addN不会
-
上面两个函数是对象本身的属性,这意味着每个Son组件都有自己的addN,如果有两个Son,就有两个addN
-
下面两个函数是对象的共用属性(也就是原型上的属性),这意味着所有Son组件共用一个addN
为什么this会变/不会变?
-
所有函数的this都是参数,由调用决定所以可变
-
唯独箭头函数的this不变,因为箭头函数不接受this
React V.S. Vue
相同点
- 都是对视图的封装,React是用类和函数表示一个组件,而Vue是通过构造选项构造一个组件
- 都提供了createElement的XML简写,React提供的是JSX语法,而Vue提供的是模板写法
不同点
- React是把HTML 放在JS里写(HTML in JS),而Vue是把JS放在HTML里写(JS in HTML)
总结
关于React的知识点
- 两种方式引入React&ReactDOM
- React.createElement(‘div’|函数|类)
- 类组件、函数组件如何获取外部数据props
- 类组件、函数组件如何使用内部数据state
- 类组件如何绑定事件,this很麻烦,使用上面两种写法搞定
- 函数组件绑定事件非常简单,不用this,React的特点:能不帮你做,就不帮你做