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

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

生命周期指的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>

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 一样,都是包管理工具,解决的都是在项目中,对文件的上传、下载、依赖描述等等相关问题。

作用npmYarn
安装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 –saveyarn upgrade
全局更新npm update –globalyarn global upgrade
卸载npm uninstall [–save/–save-dev]yarn remove xx
清除缓存npm cache cleanyarn cache clean
重装rm -rf node_modules && npm installyarn upgrade
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
综合小区管理系统管理系统按照操作主体分为管理员和用户。管理员的功能包括报修管理、车位管理、车位分配管理、出入管理、字典管理、房屋管理、物业费缴纳管理、公告管理、物业人员投诉管理、我的私信管理、物业人员管理、用户管理、管理员管理。用户的功能包括管理部门以及部门岗位信息,管理招聘信息,培训信息,薪资信息等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 综合小区管理系统管理系统可以提高综合小区管理系统信息管理问题的解决效率,优化综合小区管理系统信息处理流程,保证综合小区管理系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 管理员权限操作的功能包括管理公告,管理综合小区管理系统信息,包括出入管理,报修管理,报修管理,物业费缴纳等,可以管理操作员。 出入管理界面,管理员在出入管理界面中可以对界面中显示,可以对招聘信息的招聘状态进行查看,可以添加新的招聘信息等。报修管理界面,管理员在报修管理界面中查看奖罚种类信息,奖罚描述信息,新增奖惩信息等。车位管理界面,管理员在车位管理界面中新增。公告管理界面,管理员在公告管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值