函数组件
function Welcome(props){
return <h1>Hello,{props.name}</h1>
}
<Welcome name="jack"/>
类组件
class Welcome extends React.Component {
render() {
return <h1>Hello,{this.props.name}</h1>
}
}
<Welcome name="jack"/>
<Welcome/>会被翻译成什么?
- <div/>会被翻译为React.createElement('div)
- <Welcome/>会被翻译为React.createElement(Welcome)
React.createElement的逻辑
- 如果传入一个字符串'div',则会创建一个div
- 如果传入一个函数,则会调用该函数,获取其返回值
- 如果传入一个类,则在类前面加个new(这会导致执行constructor),获取一个组件对象,然后调用对象的render,方法,获取其返回值
类组件 VS 函数组件
我们通过+1的练习来感受函数组件和类组件数据获取的区别(内部数据)
//style.css
.App {
font-family: sans-serif;
text-align: center;
background: grey;
padding: 10px;
}
.Son {
background: yellow;
padding: 10px;
}
.Grandson {
background: green;
padding: 10px;
color: white;
}
//index.js
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(()=>{this.state.n+1})
console.log(this.state.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返回数组,第一项读,第二项写
再来看它们是怎么获取外部数据的(props)
同样地,我们通过示例来观察:
//index.js
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 messageForSon="孙子你好" />
</div>
);
}
}
const Grandson = (props) => {
return (
<div className="Grandson">我是孙子,我爸对我说:{props.messageForSon}</div>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
渲染结果如下:
通过该示例可以看出:
- 类组件直接读取属性this.props.xxx
- 函数组件直接读取参数props.xxx
复杂的state
问:如果组件里不止有n,还有m,那怎么办?
先说结论:
如果类组件里有n和m,会自动合并第一层,但是不会自动合并第二层,要使用Object.assign
或者...操作符
把先前的数据先拷贝过来,再修改。
如果函数组件里有m和n,函数组件的setX不会自动合并
还是通过练习来验证吧:
类数组件的m和n
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
class App 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>
</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,
user: {
name: "frank",
age: 18
}
};
}
addN() {
this.setState({ n: this.state.n + 1 });
// m 会被覆盖为 undefined 吗?
}
addM() {
this.setState({ m: this.state.m + 1 });
// n 会被覆盖为 undefined 吗?
}
changeUser() {
this.setState({
// m 和 n 不会被置空
user: {
name: "jack"
// age 被置空
}
});
}
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>
<hr />
<div>user.name: {this.state.user.name}</div>
<div>user.age: {this.state.user.age}</div>
<button onClick={() => this.changeUser()}>change user</button>
</div>
);
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
渲染结果如下:
通过上图可以看出: 第二层属性中的use.age
被置空了,解决方法:要使用Object.assign
或者...操作符
// 沿用以上代码,changeUser这个函数改为:
//Object.assign
changeUser() {
const user = Object.assign({}, this.state.user);
user.name = "jack";
this.setState({
// m 和 n 不会被置空
user: user
});
}
// ...操作符
changeUser() {
this.setState({
// m 和 n 不会被置空
user: {
...this.state.user, // 复制之前的所有属性
name: "jack"
// age 被置空
}
});
}
这时user.age就不会被被置空了。
函数组件的m和n
以下方式不会置空m
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Grandson />
</div>
);
}
const Grandson = () => {
const [n, setN] = React.useState(0);
const [m, setM] = React.useState(0);
return (
<div className="Son">
儿子 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);
以下这种方式m会被置空
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
return (
<div className="App">
爸爸
<Granson />
</div>
);
}
const Granson = () => {
const [state, setState] = React.useState({
n: 0,
m: 0
});
return (
<div className="Son">
儿子 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>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
可以发现,m被置空了,解决方法为: 用...操作符
// 沿用以上代码
const Granson = () => {
const [state, setState] = React.useState({
n: 0,
m: 0
});
return (
<div className="Son">
儿子 n:{state.n}
<button onClick={() => setState({...state, n: state.n + 1 })}>n+1</button>
m:{state.m}
<button onClick={() => setState({ ...state,m: state.m + 1 })}>m+1</button>
</div>
);
};
React VS Vue
相同点
- 都是相对视图的封装,React是用类和函数表示一个组件,而Vue是通过构造选项构造一个组件
- 都提供了createElemt的XML简写,React提供的是JSX语法,Vue提供的是模板写法(语法超多)
不同点
React是把HTML放在JS里写(HTML in JS),而Vue是把JS放在HTML里写(JS in HTML)
总结
- React有两种组件:函数组件和类组件
获取外部数据(props) - 类组件直接读取属性
this.props.xxx
- 函数组件直接读取参数
props.xxx
获取内部数据(state) - 类组件用
this.state
读,this.setState
写 - 函数组件用
useState
返回数组,第一项读,第二项写
复杂的state - 类组件的setState会自动合并第一层属性,但是不会合并第二层属性,可以使用
Object.assign
或...操作符
- 函数组件的setX完全不会帮你合并,合并的话就用
...操作符