02.react的生命周期和组件之间的通信

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

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

  作用     	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         
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

blueSky-fan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值