react生命周期详解

这一部分内容一直一知半解,最近发现一篇文章,非常好的解释了生命周期的问题,留存在这里,以备后查!

简介

一个react的组件Component在浏览器中可以是下面三种状态中的任意一种:Mounting(挂载)、Update(更新)、Unmounted(卸载)


这三种状态有分别对应的钩子方法,具体分类如下:

下面内容将对三种状态的钩子们进行分别注解。

Mounting(挂载)

1. 初始化state

  • 通常在ES6中,使用constructor()中初始化state。
const tom_and_jerry = [
    {
        name: 'Tom', 
        score: 55
    },
    {
        name: 'Jerry', 
        score: 80
    }
];

class ScoreBoard extends React.Component {
    constructor(props) {
        super(props);
        this.state = { players: tom_and_jerry }
    }
    
    // ...
}
  • 当然,你也可以使用ES5方法 getInitialState(),当然你要确保使用对了地方。
var ScoreBoard = React.createClass({
    getInitialState: function() {
        return {
            players: tom_and_jerry
        }
    },
    
    // ...
});

2. Default props(不常用啊)

如果父组件没有定义props的值,你可以定义默认的props值。

ES7写法:

class SinglePlayer extends React.Component {
    static defaultProps = {
        name: 'Nobody',
        score: 0
    }
    
    // ...
}

ES6写法:

class SinglePlayer extends React.Component {    
    // ...
}

SinglePlayer.defaultProps = {
    name: 'Nobody', 
    score: 0
}

ES5写法——自己定义getDefaultProps()方法:

var SinglePlayer = React.createClass({
    getDefaultProps: function() {
        return {
            name: 'Nobody', 
            score: 0
        }
    }
});

在组件创建实例之前,getDefaultProps()方法只能被调用一次。因此应避免在该钩子里使用this.props

3. componentWillMount()

该方法只在第一次渲染(render)前被加载一次,它也是一个放置初始化state值的好地方。

class SinglePlayer extends React.Component {
    componentWillMount() {
        this.setState({
            isPassed: this.props.score >= 60
        });
        
        alert('componentWillMount => ' + this.props.name);
        console.log('componentWillMount => ' + this.props.name);   
    }
    
    // ...
}

4. componentDidMount()

该方法将在每次渲染之后被执行,它也是访问DOM组件的好地方。

class ScoreBoard extends React.Component {
    constructor(props) {
        super(props);
        this._handleScroll = this.handleScroll.bind(this);
    }
    handleScroll() {}
    componentDidMount() {
        alert('componentDidMount in NoticeBoard');
        window.addEventListener('scroll', this._handleScroll);
    }
    
    // ...
}

因为每次渲染之后都会自动调用这个钩子,因此如果有哪些需要每次重新渲染都要调用的函数等都可以放在这里。

Updating(更新)

1. componentWillReceiveProps()

当一个组件正在接收新的props时,它将被调用。但是它不会在初次页面渲染(render)时被调用。

class SinglePlayer extends React.Component {
    componentWillReceiveProps(nextProps) {
        // Calculate state according to props changes
        this.setState({
            isPassed: nextProps.score >= 60
        });
    }
}

在该钩子中,旧的porps可以通过this.props获得。在通常情况下,你可以根据改变的props来设置state

2. shouldComponentUpdate()

该钩子返回的是布尔值。当一个新的props或者state正在被接收时,该钩子会在页面渲染(render)前被调用。当然它也不会在初次页面渲染(render)时被调用。
该钩子默认返回true。该钩子可以阻止不必要的重复渲染页面,以便提高性能。只需要让shouldComponentUpdate()返回false,那么组件的render()将被完全忽略,知道下一次propsstate发生改变。

class SinglePlayer extends React.Component {
    shouldComponentUpdate(nextProps, nextState) {
        // Don't rerender if score doesn't change, 
        if ( nextProps.score == this.props.score ) {
            return false;
        }

        return true;
    }
}

3. componentWillUpdate()

该钩子在shouldComponentUpdate()钩子之后(返回true),render()之前调用,当然它也不会在初次页面渲染(render)时被调用。
该钩子的作用是为更新做准备。

class SinglePlayer extends React.Component {
    componentWillUpdate(nextProps, nextState) {
        alert('componentWillUpdate => ' + this.props.name);
        console.log('componentWillUpdate => ' + this.props.name);
    }
}

4. componentDidUpdate()

调用组件的更新后立即刷新到DOM。当然它也不会在初次页面渲染(render)时被调用。
您可以执行的DOM操作后更新在这个函数。

class SinglePlayer extends React.Component {
    componentDidUpdate(prevProps, prevState) {
        alert('componentDidUpdate => ' + this.props.name);
        console.log('componentDidUpdate => ' + this.props.name);
    }
}

Unmounting(卸载)

1. componentWillUnmount()

这是前一个组件调用卸载或删除从DOM。
用这个作为一个机会来执行清理操作。例如,解开事件监听器来避免内存泄漏。

class ScoreBoard extends React.Component {
    componentWillUnmount() {
        window.removeEventListener('scroll', this._handleScroll);
    }
}

完整例子

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>React Component Lifecycle Demo</title>
    <!-- react includes two parts: react.js and react-dom.js -->
    <script src="//fb.me/react-15.2.1.js"></script>
    <script src="//fb.me/react-dom-15.2.1.js"></script>

    <!-- babel standalone -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.10.3/babel.min.js"></script>
</head>
<body>
    <div id="app"></div>

    <script type="text/babel">
        const tom_and_jerry = [
            {
                name: 'Tom',
                score: 55
            },
            {
                name: 'Jerry',
                score: 80
            }
        ];

        class SinglePlayer extends React.Component {
            constructor(props) {
                super(props);
                this.state = { isPassed: false }
            }
            componentWillMount() {
                // Mark it as 'Pass' if score >= 60
                this.setState({
                    isPassed: this.props.score >= 60
                });

                console.log('componentWillMount => ' + this.props.name);
                alert('componentWillMount => ' + this.props.name);
            }
            componentDidMount() {
                console.log('componentDidMount => ' + this.props.name);
                alert('componentDidMount => ' + this.props.name);
            }
            componentWillReceiveProps(nextProps) {
                // Calculate state according to props changes
                this.setState({
                    isPassed: nextProps.score >= 60
                });

                console.log('componentWillReceiveProps => ' + this.props.name + ': ' + nextProps.score);
                alert('componentWillReceiveProps => ' + this.props.name + ': ' + nextProps.score);
            }
            shouldComponentUpdate(nextProps, nextState) {
                // Don't rerender if score doesn't change,
                if ( nextProps.score == this.props.score ) {
                    console.log('shouldComponentUpdate => ' + this.props.name + '? false');
                    alert('shouldComponentUpdate => ' + this.props.name + '? false');
                    return false;
                }

                console.log('shouldComponentUpdate => ' + this.props.name + '? true');
                alert('shouldComponentUpdate => ' + this.props.name + '? true');
                return true;
            }
            componentWillUpdate(nextProps, nextState) {
                console.log('componentWillUpdate => ' + this.props.name);
                alert('componentWillUpdate => ' + this.props.name);
            }
            componentDidUpdate(prevProps, prevState) {
                console.log('componentDidUpdate => ' + this.props.name);
                alert('componentDidUpdate => ' + this.props.name);
            }
            componentWillUnmount() {
                console.log('componentDidUpdate => ' + this.props.name);
                alert('componentDidUpdate => ' + this.props.name);
            }
            render() {
                console.log("render => " + this.props.name);
                return (
                    <div>
                        <h5><span>Name: </span>{this.props.name}</h5>
                        <p><span>Score: </span><em>{this.props.score}</em></p>
                        <p><span>Pass: </span><input type="checkbox" defaultChecked={this.state.isPassed} disabled={true}  /></p>
                    </div>
                );
            }
        }

        class ScoreBoard extends React.Component {
            constructor(props) {
                super(props);
                this.state = {
                    players: tom_and_jerry
                };
            }
            changeScore(amount) {
                if ( typeof(amount) != "number" ) {
                    return;
                }

                let players = this.state.players;
                let tom = players[0];
                tom.score = tom.score + amount;

                tom.score = (tom.score > 100) ? 100 : tom.score;
                tom.score = (tom.score < 0) ? 0 : tom.score;

                players[0] = tom;
                this.setState({ players: players });
            }
            render() {
                return (
                    <div>
                        <h4>Score Board</h4>
                        <div>
                            <button onClick={ (amount) => this.changeScore(5) }>Score of Tom: +5</button>
                            <button onClick={ (amount) => this.changeScore(-5) }>Score of Tom: -5</button>
                        </div>
                        {
                            this.state.players.map((v, idx) => {
                                return <SinglePlayer key={idx} name={v.name} score={v.score} />
                            })
                        }
                    </div>
                );
            }
        }



        class App extends React.Component {
            render() {
                return (
                    <div>
                        <h1>React Component Lifecycle Demo</h1>
                        <ScoreBoard />
                    </div>
                )
            }
        }

        // Mount root App component
        ReactDOM.render(<App />, document.getElementById('app'));
    </script>
</body>
</html>

总结一下

  • MOUNTING

    • constructor( )——初始化props和state。
    • componentWillMount( )——我要插入啦!
    • render( )——将render里的return的内容插入到页面中。
    • componentDidMount( )——插入之后要做的事情。

MOUNTING之后,有任何的数据变动,就会进行到UPDATE过程,尤其是setState( ):

  • UPDATING
    • componentWillReceiveProps(nextProps)——我要读取props啦!
    • shouldComponentUpdate(nextProps,nextState)——请问要不要更新组件?只需要回答我true或者false就可以了。
    • render( )——更新!
    • componentDidUpdate( )——更新完毕了!

当一个组件将要从页面中移除时,就会进入UNMOUNT过程:

  • UNMOUNT

    • componentWillUnmount( )——我要死了!

TIP

setState( )一般只在下面几个钩子里使用:

  • componentWillMount( )
  • componentDidMount( )
  • componentWillReceiveProps( )

setState( )会自动调用componentDidUpdate( )钩子重新渲染页面的,具体关于它的详参这篇文章

生命周期原文章链接,点击这里 <===

THE END

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,270评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,489评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,630评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,906评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,928评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,718评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,442评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,345评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,802评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,984评论 3 337
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,117评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,810评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,462评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,011评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,139评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,377评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,060评论 2 355

推荐阅读更多精彩内容

  • Each component has several "lifecycle methods" that you c...
    暮落晨曦阅读 478评论 0 0
  • 组件生命周期,组件本质上是状态机 组件把状态和结果一一对应起来 组件中有state状态 和 props属性,属性是...
    mannysys阅读 393评论 0 0
  • Mounting / 挂载阶段 getDefaultProps->getInitialState->compone...
    TonyerX阅读 354评论 0 0
  • 1 React生命周期流程 调用流程可以参看上图。分为实例化,存在期和销毁三个不同阶段。介绍生命周期流程的文章很多...
    Dabao123阅读 322评论 0 1
  • 上面的这幅图已经很好地诠释一个react组件的生命周期,所以认真看图!认真看图!认真看图! 一、getDefaul...
    好大一颗星阅读 297评论 0 2