美文网首页
react-03-生命周期和组件通信

react-03-生命周期和组件通信

作者: 未来在奋斗 | 来源:发表于2019-12-16 17:01 被阅读0次

生命周期与钩子函数(重点)

生命周期指的react实例及组件从创建到运行到销毁的完整的过程。

组件的生命周期可分成三个阶段(状态):

  • Mounting:创建阶段:已插入真实 DOM
  • Updating:运行阶段:正在被重新渲染
  • Unmounting:销毁阶段:已移出真实 DOM

钩子函数指提前埋在生命周期中的函数,等到程序运行到这一刻时,它会自动执行。

常用的钩子函数

componentWillMount、componentWillReceiveProps、shouldComponentUpdate、ComponentDidMount

代码示例:

父组件代码

class Comp1 extends React.Component{
    constructor(){
        super();
        this.state = {
            a:1,
            isShow:true
        }
    }
    up_click(){
        this.setState(state=>({
            a:state.a+1
        }));
    }
    un_click(){
        this.setState(state=>({
            isShow:!state.isShow
        }));
    }   
    render(){
        return (<div>
            <input type="button" onClick={()=>{this.up_click()}} value="更改组件2的属性" />
            <input type="button" onClick={()=>{this.un_click()}} value="卸载组件2" />
            {this.state.isShow ? <Comp2 b={this.state.a} /> : null}
        </div>);
    }
}
ReactDOM.render( <Comp1 />, document.getElementById('root'));

子组件代码

class Comp2 extends React.Component{
    // 创建阶段
    constructor(){
        super();
        console.log("constructor");
    }
    componentWillMount(){
        console.log("componentWillMount在渲染前调用");
    }
    render(){
        console.log("render");
        return <div id='div2'>comp2-{this.props.b}</div>
    }
    componentDidMount(){
        console.log("componentDidMount在第一次渲染后调用");
    }

    // 运行中阶段
    componentWillReceiveProps(newProps) {
        console.log(`newProps: ${newProps}
        在组件接收到一个新的 prop (更新后)时被调用。这个方法在初始化render时不会被调用。`)
    }
    shouldComponentUpdate(newProps, newState) {
        console.log(`newProps: ${newProps} newState: ${newState} 
        返回一个布尔值。在组件接收到新的props或者state时被调用。
        在初始化时或者使用forceUpdate时不被调用。可以在你确认不需要更新组件时使用。`)
        return true;    // true表示更新组件;false表示不更新组件
    }
    componentWillUpdate(nextProps, nextState) {
        console.log(`nextProps: ${nextProps} nextState:${nextState} 
        在组件接收到新的props或者state但还没有render时被调用。在初始化时不会被调用。`);
    }
    componentDidUpdate(prevProps, prevState) {
        console.log(`prevProps:${prevProps} prevState:${prevState} 
        在组件完成更新后立即调用。在初始化时不会被调用。`)
    }

    // 销毁阶段
    componentWillUnmount() {
        console.log('在组件从 DOM 中移除的时候立刻被调用')
    }
}

新增的生命周期钩子函数

在 react v16.3 时,新引入了新的生命周期函数:getDerivedStateFromProps,getSnapshotBeforeUpdate。

在未来的 react v17 时,componentWillMount、componentWillReceiveProps、componentWillUpdate 要被废弃。

getDerivedStateFromProps

// 父组件的state发生变化,导致父组件中的当前组件被重新渲染,当前组件的props被修改时,该钩子函数会被触发。
/*componentWillReceiveProps(nextProps){
    console.log('nextProps: ', nextProps);
}*/


// 不仅仅具有componentWillReceiveProps的能力,自身组件state变化时,该钩子函数也会被触发。
// 该函数在shouldComponentUpdate之前执行。
// static描述是静态函数,其没有this指向,所以无权操作实例,所以更安全,而且消耗性能低。
// nextProps 传入后的prop数据,即最新的props
// prevState 相对于合并的state来说的前一个状态
static getDerivedStateFromProps(nextProps, prevState) {
    console.log('nextProps: ', nextProps);
    console.log('prevState: ', prevState);
    return {x:nextProps.a} // 合并到当前组件的state
    //return null
}

配合 componentDidUpdate 周期函数,getDerivedStateFromProps 是为了替代 componentWillReceiveProps 而出现的。它将原本 componentWillReceiveProps 功能进行划分 —— 更新 state 和 操作/调用 props,很大程度避免了职责不清而导致过多的渲染, 从而影响应该性能。

getSnapshotBeforeUpdate

在 render 之后执行的钩子

// 更新之前
getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('getSnapshotBeforeUpdate')
    console.log('prevProps:', prevProps)
    console.log('prevState:', prevState)
    return {x:1}
}

// 更新之后 snapshot能够得到 getSnapshotBeforeUpdate 的返回值
componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('componentDidUpdate')
    console.log('prevProps:', prevProps)
    console.log('prevState:', prevState)
    console.log('snapshot:', snapshot)
}

性能优化 shouldComponentUpdate()

决定视图是否需要重新渲染

改变a时,render重新执行;改变b时,render不会重新执行

class App extends Component {  
    constructor(){
        super();
        this.state = {
            a : 1,
            b : 1
        }
        this.fna = ()=>{
            this.setState({ a: new Date().getTime() })
        }
        this.fnb = function(){
            this.setState({ b: new Date().getTime() })
        }
    }
    shouldComponentUpdate(nextProps, nextState){
        if( this.state.a !== nextState.a ){
            return true;
        }else{
            return false;
        }
    }
    render(){
        return <div>
            a: {this.state.a}<br />
            b: {this.state.b}<br />
            <input type="button" value="改变a" onClick={this.fna} />
            <input type="button" value="改变b" onClick={()=>this.fnb()} />
        </div>;
    }
}

纯组件 PureComponent(浅比较)

pure 是纯的意思,PureComponent 也就是纯组件

浅比较,如果是PureComponent,那么执行add时,视图不会更新;

在修改纯组件中的状态时,检查更新前后的状态是否一致(栈中比较),如果一致,则不更新视图,如果不一致,才更新视图。

而如果是React.Component,那么当执行add时,视图会自动更新。

比较修改状态前后是否一致时,在堆中比较。

class App extends React.PureComponent {
    constructor(props) {
        super(props);
        this.state = { 
            a : 1,
            arr : ['a','b','c']    
        }
    }
    updata(){
        this.setState({
            a: this.state.a+1
        })
    }
    add(){
        this.setState(state=>{
            state.arr.push( new Date().toLocaleString() );
            return state;
        })
    }
    render() {
        return (
            <ul>
                <li>
                    <input
                        type="button" 
                        value={'修改:'+this.state.a}
                        onClick={this.updata.bind(this)} />
                    <input
                        type="button"
                        value="添加"
                        onClick={this.add.bind(this)} />
                </li>
                { this.state.arr.map((item, ind)=><li key={ind}>{item}</li>)  }
            </ul>
        );
    }
}

使用 PureComponent 可能导致不自动更新页面

因为PureComponent是浅比较,所以对数组和对象的更新,如果只是改变了堆中数据,那么系统是不会自动触发render函数的,就不会自动更新了,这个过程在react中被称为数据的突变。

把PureComponent换成Component就不会出现这个问题了,但Component属于深比较,性能消耗的多一些。

不会突变的数据力量

PureComponent浅比较中如何触发render?

只要改变了栈中的数据,视图层就会自动更新。

this.setState(prevState => ({
    words: prevState.words.concat(['marklar'])
}));
this.setState(prevState => ({
    words: [...prevState.words, 'marklar'],
}));
Object.assign({}, colormap, {right: 'blue'});

以上代码都可以在PureComponent组件中,触发render。

父组件向子组件传递数据(重点)

react 和 vue 和 angular 一样,都是单项数据流,所以项目中推荐的是父向子传递数据。

状态提升

非父子组件数据通信时,把一些共有的数据放到共有的节点中。

爷爷、大伯、父亲、孩子

孩子:修改大伯的状态时,应该把大伯的状态提升到爷爷上,然后由爷爷以属性的形式,把方法先传给父亲,然后父亲以属性的形式把方法传给孩子,孩子触发该方法,就能触发爷爷上的方法,爷爷修改了状态,重新传给大伯,大伯重新渲染页面。

父组件在属性上描述想要传入的数据即可

<abc xyz="123"></abc>

子组件使用 props 接收传入的数据即可

this.props.xyz

子组件向父组件传递数据

父组件:

fn(a, b){
    alert(a+b)
}
render(){
    return ( <div>
        父组件 <br/>
        <abc fn={this.fn.bind(this)}></abc>
    </div> )
}

子组件:

<button onClick={()=>{ this.props.fn(1,2) }} >按钮</button>

EventBus 中央事件总线

非父子组件,数据通信,eventbus。

bus.js

import { Component } from 'react'
import { EventEmitter } from 'events'
const bus = new EventEmitter();
Component.prototype.$bus = bus;

index.js

import './modules/bus.js'

创建自定义事件

this.$bus.on('abc', function(){})

触发自定义事件

this.$bus.emit('abc')

emitter对象下还有once、off等方法

Context 状态树

context 状态树虽然没有被废除,但官方是不建议使用的。

解决的是复杂组件关系时,数据共享的问题,官方建议用eventbus或redux来解决。

Provider 提供者;Consumer 消费者

// 创建名字叫做colorContext的状态树上下文对象,默认值为red。
const colorContext = React.createContext('red');

// 创建外层组件(Provider提供了一些数据共享的能力,表示colorContext这颗状态树对象的值设置为yellow)
// 即,当前组件的后代组件,都可以通过Consumer来使用共享中的数据,即yellow这个数据。
class Container extends Component {
    render(){
        return <colorContext.Provider value='yellow'>
            <div> Container
                <Temp2></Temp2>
            </div>
        </colorContext.Provider>
    }
}

// 创建中间层组件(复杂的组件关系时,这可能是很多层,如果使用context,就不需要一层一层的传递props了)
class Temp2 extends Component {
    render(){
        return <div> temp
            <Box></Box>
        </div>
    }
}

// 创建内层组件(在这层组件中,使用前面提供的数据)
class Box extends Component {
    render(){
        return <colorContext.Consumer>
            {c=><div> box
                <div style={{background:c}}>{c}</div>
            </div>}
        </colorContext.Consumer>
    }
}

把 Container 类中的 colorContext 这个标签去掉,直接在 Box 类中用 colorContext 就能够看到默认值了,注意return 后面不能有换行。

HOC 高阶组件

高阶组件(HOC)是react中的高级技术,用来重用组件逻辑。

高阶组件就是一个函数,且该函数接受一个组件作为参数,并返回一个新的组件。

// 原始组件
class OldComp extends Component {   
    render(){
        return <div>old</div>
    }
}

// 高阶组件
function higherOrderComponent(Comp){
    return class extends React.Component {
        render(){
            return <Comp />;
        }
    }
}

// 新组件
const NewComp = higherOrderComponent(OldComp);

// App组件的渲染
class App extends Component {  
    render(){
        return <NewComp />;
    }
}

Slot 插槽

基本

组合---包含关系---slot

class Component1 extends Component {   
    render(){
        return <div>
            { this.props.children }
        </div>;
    }
}

class App extends Component {
    constructor(){
        super();
        this.state = {}
    }
    render(){
        return (            
            <Component1>
                <h1>标题</h1>
                <p>内容</p>
            </Component1>
        );
    }
}

多个

class Component1 extends Component {   
    render(){
        return <div>
            { this.props.left }
            { this.props.right }
            { this.props.children }
        </div>;
    }
}

class App extends Component {
    constructor(){
        super();
        this.state = {}
    }   
    render(){
        return (            
            <Component1
                left={<div>你好</div>}
                right={<div>hello</div>}
            >
                children仅解析这内容,不会取left和right
            </Component1>
        );
    }
}

Flux 状态管理

Flux 是比较旧的一种状态管理技术,现在 react 官方已经不推荐使用了。

网上看到的 flux 教程几乎都是2015-2017年的,而当时的 react 和现在的 react 的代码写法上有很大区别。

下面是新代码的写法。

App.js 组件视图层入口

用户打开浏览器后看到的页面,这个页面有2部分功能。

  • 从 store 中获取数据,渲染到当前组件的视图层上。
  • 点击按钮,触发 action 中的方法,这个方法改变 store 中的数据。(store数据改变后,当前 App 组件重新渲染)
import React, { Component } from 'react';

// 所有的动作
import Actions from './store/actions';

// 仓库,这里保存的是数据和操作数据的方法
import store from './store/store';

// 组件
class App extends Component {
    constructor(){
        super();
        this.state = {
            todos : store.todos
        }
    }
    
    // 执行 action 中的方法
    add(){
        Actions.add('你好');
    }
    
    // 注册一个回调函数,因为 flux 中的数据修改后,不会自动更新视图,
    // 所以向 store 中注册一个函数,
    // 等 store 中数据发生变化后,要调用这个回调函数,进而修改当前视图。
    componentDidMount(){
        store.change(()=>{
            this.setState({
                todos : store.todos    
            })
        })
    }
    render() {
        return (
            <div>
                <button onClick={()=>{this.add()}}>添加</button>
                { this.state.todos.map(item=><li key={item.id}>
                    {item.text}
                </li>) }
            </div>
        );
    }
}

export default App;

action.js 动作

动作页面,所有操作 flux 的动作都写在此处,比如对 store 中某数据的增删改查操作的动作。

实际上是调用 dispatcher 的 dispatch 方法。

import appDispatcher from './dispatcher';

export default {
    add( val ){
        appDispatcher.dispatch({type:'ADD', val});
    }
}

dispatcher.js 派发

只做一些派发,业务逻辑都写在 store 中

// npm i flux  or  yarn add flux
import { Dispatcher } from 'flux';
import store from './store';

// 创建 dispatcher
const appDispatcher = new Dispatcher();

// 当用户执行appDispatcher.dispatch时,实际上执行的就是下面注册进来的函数
appDispatcher.register(action => {
    // console.log('dispatcher -> action:', action);
    switch( action.type ){
        case 'ADD':
            store.addTodo( action.val );
            store.emit('change'); // 触发用户提交过来的回调函数
            break;
    }
})

export default appDispatcher;

store.js 仓库

仓库,存储数据的容器。

import { EventEmitter } from 'events';

// 创建store对象,让store对象具有on和emit方法(Object.assign是将对象进行合并)
const store = Object.assign({}, EventEmitter.prototype, {
    todos : [],
    addTodo( val ){
        this.todos.push({text:val, id:new Date().getTime()})
    },
    change( callback ){
        this.on('change', callback);
    }
});

export default store;

Yarn

yarn 和 npm 一样,都是包管理工具,解决的都是在项目中,对文件的上传、下载、依赖描述等等相关问题。

作用 npm Yarn
安装 npm install(i) yarn
卸载 npm uninstall(un) yarn remove
全局安装 npm install xxx –-global(-g) yarn global add xxx
安装包 npm install xxx –save(-S) yarn add xxx
开发模式安装包 npm install xxx –save-dev(-D) yarn add xxx –dev(-D)
更新 npm update –save yarn upgrade
全局更新 npm update –global yarn global upgrade
卸载 npm uninstall [–save/–save-dev] yarn remove xx
清除缓存 npm cache clean yarn cache clean
重装 rm -rf node_modules && npm install yarn upgrade
react生命周期.png

相关文章

  • 09-生命周期及非父子组件间的通信

    一. Vue生命周期 二、生命周期代码 三、非父子组件通信 vue中非父子组件通信需要借助一个空的vue实例,案...

  • RN:组件

    目录一. 什么是组件二. 组件的props、state属性和普通属性三. 组件的生命周期四. 组件之间的通信方式 ...

  • 07-02-React 类组件

    目标 掌握类组件的定义和使用; 掌握 React 组件间通信的方式; 掌握类组件的生命周期。 内容 在 React...

  • VUE组件(传值,生命周期)

    VUE生命周期 VUE子传父组件通信 VUE非父子组件传值

  • 一、Android基础知识点

    四大组件是什么 四大组件的生命周期和简单用法 Activity之间的通信方式 Activity各种情况下的生命周期...

  • Android

    四大组件是什么 四大组件的生命周期和简单用法 Activity之间的通信方式 Activity各种情况下的生命周期...

  • 生命周期,组件通信、插槽

    一、生命周期 二、组件通信 Vue只有两种关系, 父子组件 非父子组件 1.父子通信:当我们需要把父组件中的数据传...

  • 2021-03-25前端面试小计,答案自行百度

    vue生命周期vue父子组件 兄弟组件通信vuexv-if和v-show区别v-if和v-for优先级wacth监...

  • 微信小程序面试题

    1.组件生命周期 2.behavior 3.全局生命周期 4.slot 5.组件通信 6.scroll-view ...

  • react组件之组件与服务器通信

    1.组件挂载阶段通信 React组件的正常运转本质上是组件不同生命周期方法的有序执行,因此组件与服务器的通信也必定...

网友评论

      本文标题:react-03-生命周期和组件通信

      本文链接:https://www.haomeiwen.com/subject/lpfenctx.html