React学习5(React class 组件)
类组件-无状态组件和有状态组件
-
1.无状态组件
- 组件本身不定义状态,没有组件生命周期,只负责 UI 渲染。
- React16.8之前的函数组件都是无状态组件,Hooks 出现后函数组件也可以有状态。
-
2.有状态组件
- 组件本身有独立数据,拥有组件生命周期,存在交互行为。
- class 组件可以定义组件自己的状态,拥有组件生命周期,它是有状态组件。
-
- 它们的区别
无状态组件由于没有维护状态只做渲染,性能较好。有状态组件提供数据和生命周期,能力更强。
- 它们的区别
-
4.如何去选择
React16.8
之前,组件不需要维护数据只渲染就使用函数组件
,有数据和交互使用类组件
。React16.8
之后,Hooks
出现给函数提供状态,建议使用函数组件即可
Rreact State(状态)
React 把组件看成是一个状态机(State Machines)。通过与用户的交互,实现不同状态,然后渲染 UI,让用户界面和数据保持一致。
React 中,class组件有自己的state来维护组件内部的数据, 只需更新class组件的 state,然后就会根据新的 state 重新渲染用户界面(不要操作 DOM)。state是私有的,完全受控于当前组件.
定义state属性定义组件状态,属于组件自己的数据,它的值是个
对象
。
使用state的时候通过this去访问即可,例如:this.state.xxx
。
数据发生变化,驱动视图更新。组件初始在构造方法内可以给state赋值,然后在其他地方只能通过this.setState()
来修改state的数据,修改数据之后会重新执行render方法进行刷新渲染视图
以下实例创建一个名称扩展为 React.Component 的 ES6 类,在 render() 方法中使用 this.state 来修改当前的时间。
添加一个类构造函数来初始化状态 this.state,类组件应始终使用 props 调用基础构造函数。
import React, {Component} from 'react';
class StateDemo extends Component {
constructor() {
super();
this.state = {
name: 'react',
now: new Date()
};
}
render() {
return (
<div>
<h1>hello {this.state.name}</h1>
<div>当前时间是: {this.state.now.toLocaleString()}</div>
<button onClick={()=>{this.setState({now: new Date()})}} >刷新时间</button>
</div>
);
}
}
React Props
state 和 props 主要的区别在于 props 是不可变的,而 state 可以根据与用户交互来改变。这就是为什么有些容器组件需要定义 state 来更新和修改数据。 而子组件只能通过 props 来传递数据。
Props的只读性
组件无论是使用函数声明还是通过 class 声明,都绝不能修改自身的 props。
React 非常灵活,但它也有一个严格的规则:
所有 React 组件都必须像纯函数一样保护它们的 props 不被更改。
默认 Props
可以通过组件类的 defaultProps 属性为 props 设置默认值,实例如下:
class Welcome extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Welcome.defaultProps = {
name: 'react'
};
const element = <Welcome/>;
ReactDOM.render(
element,
document.getElementById('root')
);
Props 验证
React.PropTypes 在 React v15.5 版本后已经移到了 prop-types 库,使用验证需要引入对应的cdn库
<script src="https://cdn.bootcss.com/prop-types/15.6.1/prop-types.js"></script>
rops 验证使用 propTypes,它可以保证我们的应用组件被正确使用,React.PropTypes 提供很多验证器 (validator) 来验证传入数据是否有效。当向 props 传入无效数据时,JavaScript 控制台会抛出警告。
class Welcome extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Welcome.propTypes = {
name: PropTypes.string
};
const element = <Welcome name={123} />;
ReactDOM.render(
element,
document.getElementById('root')
);
类组件-绑定事件
掌握类组件中绑定事件的方式,和获取事件对象的方式。
大致步骤:
在类中声明事件处理函数,在标签上使用on+事件名称={处理函数}的方式绑定事件,事件名称需要遵循大驼峰规则。
处理函数默认的参数就是事件对象,可以使用事件对象处理默认行为和事件冒泡。
class组件的创建
react的组件可以定义为class或函数的形式,class组件目前提供了更多的功能. 如需定义class组件,需要继承React.Component
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
在
React.Component
的子类中有个必须定义的render()
函数。
强烈建议不要创建自己的组件基类。 在 React 组件中,代码重用的主要方式是组合而不是继承.
React 并不会强制你使用 ES6 的 class 语法。如果你倾向于不使用它,你可以使用 create-react-class 模块或类似的自定义抽象来代替。请查阅不使用 ES6 了解更多
组件的生命周期
每个组件都包含“生命周期方法”,你可以重写这些方法,以便于在运行过程中特定的阶段执行这些方法。在下述列表中,常用的生命周期方法会被加粗。(生命周期图谱)
函数的生命周期 主要分为三个阶段 :挂载
(已插入真实 DOM)、更新
(正在被重新渲染)、卸载
(已移出真实DOM) .
class LifeDemo extends Component {
constructor() {
super();
console.log('======执行了构造方法');
// 构造方法中直接给state赋初始值 不能执行setState
this.state = {name:'react'}
}
componentWillMount() {
console.log('=======组件将要挂载,执行了componentWillMount')
}
componentDidMount() {
console.log('======组件挂载到dom节点完成,执行了componentDidMount方法');
}
shouldComponentUpdate(nextProps, nextState, nextContext) {
console.log('=====执行了shouldComponentUpdate 组件在受到props或者state更新要判断是否执行render方法刷新视图, 返回false则不执行render');
// 当state中name变为vue时,视图不更新
if (nextState.name === 'vue') {
return false;
}
return true;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('=====组件更新完成之后,执行了componentDidUpdate')
}
componentWillUnmount() {
// 一般用于处理消除定时器、订阅
console.log('======组件将要移除时,执行componentWillUnmount')
}
render() {
console.log('======执行了render方法');
return (
<div>
组件生命周期Demo
<div>hello {this.state.name}</div>
<input type="text" value={this.state.name}
onChange={(e)=>{
console.log(e.target.value);
console.log(this);
this.setState({name: e.target.value})
}} />
</div>
);
}
}
挂载
当组件实例被创建并插入DOM 中时,生命周期调用顺序如下:
constructor()
- static getDerivedStateFromProps()
render()
componentDidMount()
下述生命周期方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillMount()
更新
当组件的 props 或 state 发生变化时会触发更新。组件更新的生命周期调用顺序如下:
- static getDerivedStateFromProps()
- shouldComponentUpdate()
render()
- getSnapshotBeforeUpdate()
componentDidUpdate()
下述方法即将过时,在新代码中应该避免使用它们:
UNSAFE_componentWillUpdate()
UNSAFE_componentWillReceiveProps()
更新
当组件从 DOM 中移除时会调用如下方法:
componentWillUnmount()
错误处理
当渲染过程,生命周期,或子组件的构造函数中抛出错误时,会调用如下方法:
- static getDerivedStateFromError()
- componentDidCatch()
其他 APIs
setState()
- forceUpdate()
class 属性
- defaultProps
- displayName
实例属性
props
state
常用生命周期方法
render():
render()
render() 方法是 class 组件中唯一必须实现的方法。
当 render 被调用时,它会检查 this.props 和 this.state 的变化并返回以下类型之一:
React 元素
。通常通过 JSX 创建。例如,<div />
会被 React 渲染为 DOM 节点,<Welcome/>
会被 React 渲染为自定义组件,无论是<div />
还是<Welcome/>
均为 React 元素。数组或 fragments
。 使得 render 方法可以返回多个元素。欲了解更多详细信息,请参阅 fragments 文档。Portals
。可以渲染子节点到不同的 DOM 子树中。(Portals是一个可以插入指定dom节点下的element元素)欲了解更多详细信息,请参阅有关 portals 的文档字符串或数值类型
。它们在 DOM 中会被渲染为文本节点布尔类型或 null
。什么都不渲染。(主要用于支持返回 test && 的模式,其中 test 为布尔类型。)
render() 函数应该为纯函数,这意味着在不修改组件 state 的情况下,每次调用时都返回相同的结果,并且它不会直接与浏览器交互。
如需与浏览器进行交互,请在 componentDidMount() 或其他生命周期方法中执行你的操作。保持 render() 为纯函数,可以使组件更容易思考。
在组件更新阶段,执行render之前会调用
shouldComponentUpdate()
方法, 如果 shouldComponentUpdate() 返回false
,则不会调用 render()。
constructor()
constructor(props)
如果不初始化 state 或不进行方法绑定,则不需要为 React 组件实现构造函数。
在 React 组件挂载之前,会调用它的构造函数。在为 React.Component 子类实现构造函数时,应在其他语句之前前调用 super(props)。否则,this.props 在构造函数中可能会出现未定义的 bug。
通常,在 React 中,构造函数仅用于以下两种情况:
- 通过给 this.state 赋值对象来初始化内部 state。
- 为事件处理函数绑定实例
在 constructor() 函数中**不要调用 setState() **方法。如果组件需要使用内部 state,请直接在构造函数中为 this.state 赋值初始 state: 只能在构造函数中直接为 this.state 赋值。如需在其他方法中赋值,你应使用 this.setState() 替代。
constructor(props) {
super(props);
// 不要在这里调用 this.setState()
this.state = { counter: 0 };
this.handleClick = this.handleClick.bind(this);
}
componentDidMount()()
componentDidMount()
componentDidMount()
会在组件挂载后(插入 DOM 树中)立即调用。依赖于 DOM 节点的初始化应该放在这里。如需通过网络请求获取数据,此处是实例化请求的好地方。
这个方法是比较适合添加订阅的地方。如果添加了订阅,请不要忘记在 componentWillUnmount()
里取消订阅
可以在 componentDidMount()
里直接调用 setState()。它将触发额外渲染,但此渲染会发生在浏览器更新屏幕之前。如此保证了即使在 render()
两次调用的情况下,用户也不会看到中间状态。请谨慎使用该模式,因为它会导致性能问题。通常,你应该在 constructor()
中初始化 state。如果渲染依赖于 DOM 节点的大小或位置,比如实现 modals 和 tooltips 等情况下,你可以使用此方式处理
componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot)
componentDidUpdate()
会在更新后会被立即调用。首次渲染不会执行此方法。
当组件更新后,可以在此处对 DOM 进行操作。如果你对更新前后的 props 进行了比较,也可以选择在此处进行网络请求。(例如,当 props 未发生变化时,则不会执行网络请求)。
componentDidUpdate(prevProps) {
// 典型用法(不要忘记比较 props):
if (this.props.userID !== prevProps.userID) {
this.fetchData(this.props.userID);
}
}
你也可以在 componentDidUpdate()
中直接调用 setState(),但请注意它必须被包裹在一个条件语句里,正如上述的例子那样进行处理,否则会导致死循环。它还会导致额外的重新渲染,虽然用户不可见,但会影响组件性能。不要将 props “镜像”给 state,请考虑直接使用 props。
如果组件实现了 getSnapshotBeforeUpdate()
生命周期(不常用),则它的返回值将作为 componentDidUpdate() 的第三个参数 “snapshot” 参数传递。否则此参数将为 undefined。
如果 shouldComponentUpdate() 返回值为 false,则不会调用 componentDidUpdate()。