让前端飞Web前端之路Web 前端开发

react生命周期详解

2017-09-27  本文已影响323人  春木橙云

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

简介

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


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

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

Mounting(挂载)

1. 初始化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 }
    }
    
    // ...
}
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之后,有任何的数据变动,就会进行到UPDATE过程,尤其是setState( ):

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

TIP

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

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

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

THE END

上一篇下一篇

猜你喜欢

热点阅读