React框架必须掌握的核心基础知识(二) —— 生命周期与组件通信

471 阅读12分钟

1、组件的生命周期

1.1、组件生命周期的三个阶段

  1. Mounting(加载阶段)
  2. Updating(更新阶段)
  3. 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、总结

旧的生命周期

图片描述
新的生命周期
图片描述

  1. React16新的生命周期弃用了componentWillMount、componentWillReceivePorps,componentWillUpdate
  2. 新增了getDerivedStateFromProps、getSnapshotBeforeUpdate来代替弃用的三个钩子函数(componentWillMount、componentWillReceivePorps,componentWillUpdate)
  3. React16并没有删除这三个钩子函数,但是不能和新增的钩子函数(getDerivedStateFromProps、getSnapshotBeforeUpdate)混用,React17将会删除componentWillMount、componentWillReceivePorps,componentWillUpdate
  4. 新增了对错误的处理(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() 方法 returnJSX 代码需要在最外层使用一个标签包裹,如果不想在页面中显示最外层的这个标签,可以使用 <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;