创建 Class 组件
-
ES5 方式(过时)
import React from 'react' const A = React.createClass({ render(){ return(<div>hi</div>) } }) export default A // 由于 ES5 不支持 class,才会有这种方法
-
ES6 方式
import React from 'react' class B extends React.Component{ constructor(props){ super(props) } render(){ return (<div>hi</div>) } } export default B
props
传入 props 给 B 组件
class Parent extends React.Component {
constructor(props){
super(props)
this.state = {name: 'frank'}
}
onClick = () => {}
render(){
return(
<B name={this.state.n} onClick={this.onClick}>hi</B>
)
}
}
// 外部数据被封装为一个对象
// {name: 'frank', onClick:...,children: 'hi'}
初始化
class B extends React.Component{
constructor(props){
super(props)
}
render(){...}
}
// this.props 就是外部数据 对象的地址 了
读取
class B extends React.Component{
constructor(props){
super(props)
}
render(){
return(
<div onClick={this.props.onClick}>
{this.props.name}
<div>{this.props.chrldren}</div>
</div>
)
}
}
// 通过 this.props.xxx 读取
props 的作用
接受外部数据
- 只能读不能写
- 外部数据由父组件传递
接受外部函数
- 在恰当的时机,调用该函数
- 该函数一般是父组件的函数
state & setState
初始化
class B extends React.Component {
constructor(props){
super(props)
this.state = {
user: {
name: 'frank',
age: 18
}
}
}
render(){...}
}
读写 State
读 this.state
this.state.user.name
写 this.setState(???,fn)
this.setState(newState, fn)
注意 setState 不会立刻改变 this.state,会在当前代码运行完后,再去更新 this.state,从而触发 UI 更新。(异步)
this.setState((state, props) => newState, fn)
这种方式的 state 更容易理解
fn 会在写入成功后执行。
shallow merge: setState 会自动将新的 state 与 旧的 state 进行一级合并。
onClick = () => {
this.setState({ x: this.state.x + 1 });
};
onClick2 = () => {
this.setState( (state) => ({ x: state.x + 2 }) );
};
onClick = () => {
this.setState(
{
x: this.state.x + 1,
},
// callback
() => {
this.setState({ x: this.state.x + 1 });
}
);
};
修改 this.state 的属性值,不推荐用!
this.state.x += 1
this.setState(this.state)
生命周期
类比如下代码
let div = document.createElement('div')
这是 div 的 create / construct 过程
div.textContent = 'hi'
这是初始化 state
document。body.appendChild(div)
这是 div 的 mount 过程
div.textContent = 'hi2'
这是 div 的 update 过程
div.remove()
这是 div 的 unmount 过程
函数列表
constructor()
static getDerivedStateFromProps()
shouldComponentUpdate()
render()
getSnapshotBeforeUpdate()
componentDidMount()
componentDidUpdate()
componentWillUnmount()
static getDerivedStateFromError()
componentDidCatch()
函数列表-必会
constructor()
- 在这里初始化 state
shouldComponentUpdate()
- return false 阻止更新
render()
- 创建虚拟 DOM
componentDidMount()
- 组件已出现在页面
componentDidUpdate()
- 组件已更新
componentWillUnmount()
- 组件将死
constructor
用途:
初始化 props
初始化 state,但此时不能调用 setState
用来写 bind this
constructor(){
...
this.onClick = this.onClick.bind(this)
}
可以用新语法代替
constructor(){...}
onClick = () => {}
shouldComponentUpdate
用途:
返回 true 表示不阻止 UI 更新
返回 false 表示阻止 UI 更新
面试常问:shouldComponentUpdate 有什么用?
答:它允许我们手动判断是否要进行组件更新,我们可以根据应用场景灵活地设置返回值,以避免不必要地更新。
import React from "react";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
n: 1,
};
}
onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
this.setState((state) => ({ n: state.n - 1 }));
};
// {n:1} 和 {n:1}
shouldComponentUpdate(nextProps, nextState) {
// 阻止渲染
if (nextState.n === this.state.n) {
return false;
} else {
return true;
}
}
render() {
console.log("render了一次");
return (
<div>
App组件<br />
{this.state.n}
<button onClick={this.onClick}>+1</button>
</div>
);
}
}
export default App;
可以使用 React.PureComponent
替代 React.Component
实现 shouldComponentUpdate()
PureComponent 会在 render 之前对比新 state 和旧 state 的每一个 key,以及新 props 和旧 props 的每一个 key。
如果所有 key 的值全都一样,就不会 render;如果有任何一个 key 的值不同,就会 render。
注意
React.PureComponent
中的shouldComponentUpdate()
仅作对象的浅层比较。如果对象中包含复杂的数据结构,则有可能因为无法检查深层的差别,产生错误的比对结果。仅在你的 props 和 state 较为简单时,才使用
React.PureComponent
。
render
用途:
展示视图 return (<div> ... </div>)
,只能有一个根元素。
两个根元素,要用 <React.Fragment>
包起,可以缩写为 <> </>
技巧:
render 里面可以写 if...else
、?:
表达式
不能直接写 for
循环,需要用数组
render 里面可以写 aray.map
(循环)
class App extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
n: 1,
array: [1, 2, 3]
};
}
onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
};
// if...else
render() {
let message
if(this.state.n%2 === 0){
message = <span>偶数</span>
}else{
message = <span>奇数</span>
}
return(
<React.Fragment>
{message}
<button onClick={this.onClick}>+1</button>
</React.Fragment>
)
}
// ? :
render() {
return (
<>
{this.state.n % 2 === 0 ?
<span>偶数</span> :
<span>奇数</span>}
<button onClick={this.onClick}>+1</button>
</>
);
}
// for 循环
render() {
let result = [];
for (let i = 0; i < this.state.array.length; i++) {
result.push(this.state.array[i]);
}
return result;
}
// for 循环 map()
render() {
return this.state.array.map((n) => <span key={n}>{n}</span>);
}
}
componentDidMount()
用途:
在元素插入页面后执行代码,这些代码依赖 DOM
比如,想获取 div 的高度,最好在这里写。
此处可以发起加载数据的 Ajax 请求(官方推荐)
首次渲染 会执行此钩子
// 想获取 div 的高度
...
componentDidMount(){
const div = document.querySelector('#xxx');
const {width} = div.getBoundingClientRect()
this.setState({width})
}
render() {
return(
<div id='xxx'>hello, width, {this.state.width}px</div>
)
}
改用 ref
class App extends React.PureComponent {
divRef = undefined;
constructor(props) {
super(props);
this.state = {
n: 1,
width: undefined,
};
this.divRef = React.createRef();
}
onClick = () => {
this.setState((state) => ({ n: state.n + 1 }));
};
componentDidMount() {
const div = this.divRef.current;
const { width } = div.getBoundingClientRect();
this.setState({ width });
}
render() {
return <div ref={this.divRef}>hello, width: {this.state.width}px</div>;
}
}
componentDidUpdate()
用途:
在视图更新后执行。第一次渲染不执行。
此处也可以发起 Ajax 请求,用于更新数据 (文档)。
首次渲染不会 执行此钩子。
在此处 setState 可能会引起无限循环,除非放在 if 里。
若 shouldComponentUpdate 返回 false,则不会触发此钩子。
componentWillUnmount()
用途:
组件将要 被移出页面 然后被销毁 时执行代码
unmount 过的组件不会再次 mount
举例:
-
如果你在 c…DidMount 里面监听了 window scroll
那么你就要在 c…WillUnmount 里面取消监听
-
如果你在 c…DidMount 里面创建了 Timer
那么你就要在 c…WillUnmout 里面取消 Timer
-
如果你在 c…DidMount 里面创建了 Ajax 请求
那么你就要在 c…WillUnmount 里面取消请求
-
否则你就是菜逼。
生命周期回顾
-
constructor()
- 在这里初始化 state -
shouldComponentUpdate()
- return false 阻止更新 -
render()
- 创建虚拟 DOM -
componentDidMount()
- 组件已出现在页面 -
componentDidUpdate()
- 组件已更新 -
componentWillUnmount()
- 组件将死