1、组件的生命周期
1.1、组件生命周期的三个阶段
- Mounting(加载阶段)
- Updating(更新阶段)
- Unmounting(卸载阶段)
1.2、旧的生命周期
Mounting(加载阶段:涉及6个钩子函数)
constructor()
加载的时候调用一次,可以初始化state
getDefaultProps()
设置默认的props,也可以用dufaultProps设置组件的默认属性。
getInitialState()
初始化state,可以直接在constructor中定义this.state
componentWillMount()
组件加载时只调用,以后组件更新不调用,整个生命周期只调用一次,此时可以修改state
render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行
componentDidMount()
组件渲染之后调用,只调用一次
Updating(更新阶段:涉及5个钩子函数)
componentWillReceivePorps(nextProps)
组件加载时不调用,组件接受新的props时调用
shouldComponentUpdate(nextProps, nextState)
组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)
componentWillUpdata(nextProps, nextState)
组件加载时不调用,只有在组件将要更新时才调用,此时可以修改state
render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行
componentDidUpdate()
组件加载时不调用,组件更新完成后调用
Unmounting(卸载阶段:涉及1个钩子函数)
componentWillUnmount()
组件渲染之后调用,只调用一次
组件的基本写法
import React, { Component } from 'react'
export default class OldReactComponent extends Component {
constructor(props) {
super(props)
// getDefaultProps:接收初始props
// getInitialState:初始化state
}
state = {
}
componentWillMount() { // 组件挂载前触发
}
render() {
return (
<h2>Old React.Component</h2>
)
}
componentDidMount() { // 组件挂载后触发
}
componentWillReceivePorps(nextProps) { // 接收到新的props时触发
}
shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
return true
}
componentWillUpdate(nextProps, nextState) { // 组件更新前触发
}
componentDidUpdate() { // 组件更新后触发
}
componentWillUnmount() { // 组件卸载时触发
}
}
1.3、新的生命周期
Mounting(加载阶段:涉及4个钩子函数)
constructor()
加载的时候调用一次,可以初始化state
static getDerivedStateFromProps(props, state)
组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法
render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行
componentDidMount()
组件渲染之后调用,只调用一次
Updating(更新阶段:涉及5个钩子函数)
static getDerivedStateFromProps(props, state)
组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state;配合componentDidUpdate,可以覆盖componentWillReceiveProps的所有用法
shouldComponentUpdate(nextProps, nextState)
组件接收到新的props或者state时调用,return true就会更新dom(使用diff算法更新),return false能阻止更新(不调用render)
render()
react最重要的步骤,创建虚拟dom,进行diff算法,更新dom树都在此进行
getSnapshotBeforeUpdate(prevProps, prevState)
触发时间: update发生的时候,在render之后,在组件dom渲染之前;返回一个值,作为componentDidUpdate的第三个参数;配合componentDidUpdate, 可以覆盖componentWillUpdate的所有用法
componentDidUpdate()
组件加载时不调用,组件更新完成后调用
Unmounting(卸载阶段:涉及1个钩子函数)
组件渲染之后调用,只调用一次
Error Handling(错误处理)
componentDidCatch(error,info)
任何一处的javascript报错会触发
组件的基本写法
import React, { Component } from 'react'
export default class NewReactComponent extends Component {
constructor(props) {
super(props)
// getDefaultProps:接收初始props
// getInitialState:初始化state
}
state = {
}
static getDerivedStateFromProps(props, state) { // 组件每次被rerender的时候,包括在组件构建之后(虚拟dom之后,实际dom挂载之前),每次获取新的props或state之后;;每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state
return state
}
componentDidCatch(error, info) { // 获取到javascript错误
}
render() {
return (
<h2>New React.Component</h2>
)
}
componentDidMount() { // 挂载后
}
shouldComponentUpdate(nextProps, nextState) { // 组件Props或者state改变时触发,true:更新,false:不更新
return true
}
getSnapshotBeforeUpdate(prevProps, prevState) { // 组件更新前触发
}
componentDidUpdate() { // 组件更新后触发
}
componentWillUnmount() { // 组件卸载时触发
}
}
1.4、总结
旧的生命周期
- React16新的生命周期弃用了componentWillMount、componentWillReceivePorps,componentWillUpdate
- 新增了getDerivedStateFromProps、getSnapshotBeforeUpdate来代替弃用的三个钩子函数(componentWillMount、componentWillReceivePorps,componentWillUpdate)
- React16并没有删除这三个钩子函数,但是不能和新增的钩子函数(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17将会删除componentWillMount、componentWillReceivePorps,componentWillUpdate
- 新增了对错误的处理(componentDidCatch)
2、父子组件传值(组件通信)
2.1、父组件传值子组件
在引用子组件的时候传递,相当于一个属性,例如:在子组件内通过porps.param获取到这个param的值。
父组件向子组件传值,通过props,将父组件的state传递给了子组件。
父组件代码片段:
constructor(props){
super(props)
this.state={
message:"i am from parent"
}
}
render(){
return(
<Child txt={this.state.message}/>
)
}
}
子组件代码片段:
render(){
return(
<p>{this.props.txt}</p>
)
}
完整示例
创建父组件 index.js
import React from 'react';
import ReactDOM from 'react-dom';
import User from './User';//引入子组件
//定义数据
const person = {
name: 'Tom',
age:20
}
ReactDOM.render(
//渲染子组件,并向子组件传递name,age属性
<User name={person.name} age={person.age}></User>
, document.getElementById('root'));
创建子组件 User.js
import React from 'react';
class User extends React.Component{
render(){
return (
// 使用props属性接收父组件传递过来的参数
<div>{this.props.name},{this.props.age}</div>
);
}
}
export default User;
在父组件中可以使用展开运算符 ... 传递对象
index.js文件
ReactDOM.render(
//渲染子组件,并向子组件传递name,age属性
<User {...person}></User>
, document.getElementById('root'));
User.js文件
render(){
return (
// 使用props属性接收父组件传递过来的参数
<div>{this.props.name},{this.props.age}</div>
);
}
2.2、子组件传值父组件
子组件通过调用父组件传递到子组件的方法向父组件传递消息的。
完整案例
子组件 Son.js 文件代码示例:
import React from 'react';
class Son extends React.Component {
//构造方法
constructor(){
super();
this.state = {
inputValue:''
}
}
//按钮点击事件
handleClick(){
//通过props属性获取父组件的getdata方法,并将this.state值传递过去
this.props.getdata(this.state.inputValue);
}
//输入框事件,用于为this.state赋值
handleChange(e){
this.setState({
inputValue: e.target.value
});
}
render(){
return (
<React.Fragment>
<input onChange={this.handleChange.bind(this)}></input>
<button onClick={this.handleClick.bind(this)}>点击获取数据</button>
</React.Fragment>
);
}
}
export default Son;
父组件 Parent.js 文件代码示例:
import React from 'react';
import Son from './Son';
class Parent extends React.Component {
//构造方法
constructor(){
super();
this.state = {
mess: '' //初始化mess属性
}
}
//用于接收子组件的传值方法,参数为子组件传递过来的值
getDatas(msg){
//把子组件传递过来的值赋给this.state中的属性
this.setState({
mess: msg
});
}
render(){
return (
<React.Fragment>
{/* 渲染子组件,设置子组件访问的方法,
getdata属性名为子组件中调用的父组件方法名 */}
<Son getdata={this.getDatas.bind(this)}></Son>
<div>展示数据:{this.state.mess}</div>
</React.Fragment>
);
}
}
export default Parent;
入口文件 index.js示例代码:
import React from 'react';
import ReactDOM from 'react-dom';
import Parent from './Parent';
ReactDOM.render(<Parent></Parent>, document.getElementById('root'));
2.3、兄弟(同级)组件传值
兄弟组件之间的传值,是通过父组件做的中转 ,流程为:
组件A -- 传值 --> 父组件 -- 传值 --> 组件B
代码示例:
创建 Acls.js 组件,用于提供数据
import React from 'react';
class Acls extends React.Component {
//按钮点击事件,向父组件Pcls.js传值
handleClick(){
this.props.data("hello...React...");
}
render(){
return (
<button onClick={this.handleClick.bind(this)}>Acls组件中获取数据</button>
);
}
}
export default Acls;
创建父组件 Pcls.js 用于中转数据
import React from 'react';
import Acls from './Acls';
import Bcls from './Bcls';
class Pcls extends React.Component {
//构造函数
constructor(){
super();
this.state = {
mess: ''
}
}
//向子组件Acls.js提供的传值方法,参数为获取的子组件传过来的值
getDatas(data){
this.setState({
mess: data
});
}
render(){
return (
<React.Fragment>
Pcls组件中显示按钮并传值:
<Acls data={this.getDatas.bind(this)}></Acls>
<Bcls mess={this.state.mess}></Bcls>
</React.Fragment>
);
}
}
export default Pcls;
创建子组件 Bcls.js 用于展示从 Acls.js 组件中生成的数据
import React from 'react';
class Bcls extends React.Component {
render(){
return (
<div>在Bcls组件中展示数据:{this.props.mess}</div>
);
}
}
export default Bcls;
3、组件通信案例--TodoList
3.1.创建 Todolist.js 组件
import React from 'react';
class TodoList extends React.Component{
//构造方法
constructor(props){
super(props);
this.state = {
list: [
'learn html',
'learn css',
'learn react',
'learn vue'
]
}
}
//按钮点击事件方法
handleBtnClick(){
this.setState({
list: [...this.state.list,'hello world'] //...为展开运算符,将this.state.list内容放到当前的list中
});
}
render(){
return (
<div>
<div>
<input type="text"></input>
<button onClick={this.handleBtnClick.bind(this)}>添加</button>
</div>
<ul>
{/* key属性为唯一值,没有该属性,浏览器会报警告信息,在做添加操作时会出bug */}
{this.state.list.map((item,index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
}
export default TodoList;
引用组件
import React from 'react';
import ReactDOM from 'react-dom';
import TodoList from './TodoList';
ReactDOM.render(<TodoList />, document.getElementById('root'));
3.2.添加列表项功能
代码示例:
import React from 'react';
class TodoList extends React.Component{
//构造方法
constructor(props){
super(props);
this.state = {
list: [], //展示列表
inputValue:'' //记录输入框的值
}
}
////按钮点击事件方法
handleBtnClick(){
this.setState({
list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
inputValue: '' //点击添加按钮,清空输入框
});
}
//输入框输入事件方法
handleInputChange(e){
this.setState({
inputValue: e.target.value
});
}
render(){
return (
<div>
<div>
<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
<button onClick={this.handleBtnClick.bind(this)}>添加</button>
</div>
<ul>
{this.state.list.map((item,index) => <li key={index}>{item}</li>)}
</ul>
</div>
);
}
}
export default TodoList;
3.3.删除列表元素
删除列表元素代码:
import React from 'react';
class TodoList extends React.Component{
//构造方法
constructor(props){
super(props);
this.state = {
list: [], //展示列表
inputValue:'' //记录输入框的值
}
}
////按钮点击事件方法
handleBtnClick(){
this.setState({
list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
inputValue: '' //点击添加按钮,清空输入框
});
}
//输入框输入事件方法
handleInputChange(e){
this.setState({
inputValue: e.target.value
});
}
//点击展示列表事件方法,用于删除展示元素
handleItemClick(index){
const list = [...this.state.list];
list.splice(index,1);
this.setState({
list: list
});
}
render(){
return (
<div>
<div>
<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
<button onClick={this.handleBtnClick.bind(this)}>添加</button>
</div>
<ul>
{this.state.list.map((item,index) => {
return <li onClick={this.handleItemClick.bind(this,index)} key={index}>
{item}
</li>
})}
</ul>
</div>
);
}
}
export default TodoList;
3.4.使用组件化实现删除功能
创建子组件 TodoItem.js
import React from 'react';
class TodoItem extends React.Component{
//点击元素删除的方法
//子组件如果想和父组件通信,子组件要调用父组件传递过来的方法
handleDelete(){
//调用父组件的方法,向父组件传值
this.props.delete(this.props.index);
}
render(){
return (
<div onClick={this.handleDelete.bind(this)}>
{/* 子组件通过props接收父组件传递过来的参数 */}
{this.props.content}
</div>
);
}
}
export default TodoItem;
在父组件 TodoList.js 中调用子组件
import React from 'react';
import TodoItem from './TodoItem';
class TodoList extends React.Component{
//构造方法
constructor(props){
super(props);
this.state = {
list: [], //展示列表
inputValue:'' //记录输入框的值
}
}
////按钮点击事件方法
handleBtnClick(){
this.setState({
list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
inputValue: '' //点击添加按钮,清空输入框
});
}
//输入框输入事件方法
handleInputChange(e){
this.setState({
inputValue: e.target.value
});
}
//点击元素删除的方法,该方法是用来接收子组件的传值
handelDeleteItem(index){
const list = [...this.state.list];
list.splice(index,1);
this.setState({
list: list
});
}
render(){
return (
<div>
<div>
<input value={this.state.inputValue} onChange={this.handleInputChange.bind(this)}></input>
<button onClick={this.handleBtnClick.bind(this)}>添加</button>
</div>
<ul>
{this.state.list.map((item,index) => {
//父组件通过属性的形式向子组件传递参数
return <TodoItem
delete={this.handelDeleteItem.bind(this)}
key={index}
content={item}
index={index}>
</TodoItem>
})}
</ul>
</div>
);
}
}
export default TodoList;
3.5.代码优化
TodoItem.js 代码优化:
import React from 'react';
class TodoItem extends React.Component{
//构造方法
constructor(props){
super(props);
this.handleDelete = this.handleDelete.bind(this);
}
//点击元素删除的方法
//子组件如果想和父组件通信,子组件要调用父组件传递过来的方法
handleDelete(){
const { handelDelete , index } = this.props;
//调用父组件的方法,向父组件传值
handelDelete(index);
}
render(){
const { content } = this.props;
return (
<div onClick={this.handleDelete}>{content}</div>
);
}
}
export default TodoItem;
TodoList.js 代码优化:
import React from 'react';
import TodoItem from './TodoItem';
class TodoList extends React.Component{
//构造方法
constructor(props){
super(props);
this.state = {
list: [], //展示列表
inputValue:'' //记录输入框的值
}
this.handleInputChange = this.handleInputChange.bind(this);
this.handleBtnClick = this.handleBtnClick.bind(this);
this.handelDeleteItem = this.handelDeleteItem.bind(this);
}
////按钮点击事件方法
handleBtnClick(){
this.setState({
list: [...this.state.list,this.state.inputValue], //...为展开运算符,将this.state.list内容放到当前的list中
inputValue: '' //点击添加按钮,清空输入框
});
}
//输入框输入事件方法
handleInputChange(e){
this.setState({
inputValue: e.target.value
});
}
//点击元素删除的方法,该方法是用来接收子组件的传值
handelDeleteItem(index){
const list = [...this.state.list];
list.splice(index,1);
this.setState({
list: list
});
}
//遍历方法
getTodoItems(){
return (
this.state.list.map((item,index) => {
//父组件通过属性的形式向子组件传递参数
return <TodoItem
handelDelete={this.handelDeleteItem}
key={index}
content={item}
index={index} />
})
);
}
render(){
return (
<div>
<div>
<input value={this.state.inputValue} onChange={this.handleInputChange}></input>
<button onClick={this.handleBtnClick}>添加</button>
</div>
<ul>
{this.getTodoItems()}
</ul>
</div>
);
}
}
export default TodoList;
3.6.使用CSS样式修饰
方法一:使用style属性
代码示例:
<button style={{background:'blue',color:'#fff'}} onClick={this.handleBtnClick}>
添加
</button>
方法二:使用className属性
创建 style.css 文件
.red-btn{
background-color: red;
color: #ffffff;
}
在 index.js 入口文件引入 css
import './style.css';
在组件中使用 className 属性
<button className='red-btn' onClick={this.handleBtnClick}>添加</button>
3.7.JSX代码优化
render() 方法 return 的 JSX 代码需要在最外层使用一个标签包裹,如果不想在页面中显示最外层的这个标签,可以使用 <React.Fragment> 标签替代,代码示例:
render(){
return (
<React.Fragment>
<div>
<input />
<button>添加</button>
</div>
<ul>
{this.getTodoItems()}
</ul>
</React.Fragment>
);
}
也可以在引入组件时直接引入类,在使用时就不需要用 React 调用
引入
import React,{Component,Fragment} from 'react';
class TodoList extends Component{
render(){
return (
<Fragment>
<div>
<input />
<button>添加</button>
</div>
<ul>
{this.getTodoItems()}
</ul>
</Fragment>
);
}
}
export default TodoList;