• React生命周期


    一、生命周期

    生命周期分成三个阶段,下面这些函数中带有will前缀的是在事情发生前调用的,带有did前缀的是在事情发生后调用的。

    • Mounting阶段,当一个组件的实例被创建并插入到DOM中时,下面这些函数会被调用:
      • constructor()
      • componentWillMount:组件即将被渲染到页面上,render之前最后一次修改状态的机会
      • render:生成虚拟的DOM节点,只能访问this.props和this.state,只有一个顶层组件,不允许修改状态和DOM输出
      • componentDidMount:组件已经渲染到页面上,成功render并渲染完成真实的DOM之后触发,可以修改DOM
    • Updating阶段,属性和状态的修改会导致更新,当一个组件被re-render时会调用以下函数:
      • componentWillRecieveProps:组件将要接收到属性时调用,父组件修改属性触发,可以修改新属性、修改状态
      • shouldComponentUpdate:当组件接受到新属性或新状态时触发,判断组件是否需要更新,返回false会阻止render调用
      • componentWillUpdate:组件将要更新,不能修改属性和状态
      • render:同上
      • componentDidUpdate:组件已经更新,可以修改DOM
    • Unmounting阶段,当一个组件被移出DOM时调用
      • componentWillUnmount:在删除组件之前进行清理操作,比如计时器和事件监听器

    1. render()

    render()函数是必须的。当调用render()函数时,会检查this.props和this.state,然后返回一个React element。render()函数也可以返回null或者false,表示不需要渲染任何东西。当返回是null或者false时,ReactDOM.findDOMNode(this)也会返回null。另外render()函数必须是pure的,意味着不能在render()函数中修改组件state,每次调用render时返回同样的结果。并且render()不能直接与浏览器交互,如果需要与浏览器交互,应该将这些交互代码放在componentDidMount()或者其他生命周期方法中。

    注意:如果shouldComponentUpdate()返回false则render()不会被调用。

    2. constructor(props)

    React组件mounted前会调用constructor。当实现constructor函数时,必须在函数的第一行调用super(props)。否则this.props会是undefined。constructor是初始化state,bind函数的地方,如果你的组件不需要初始化state,bind函数,那么可以不需要constructor函数。

    根据props初始化state也是可以的,只要你知道怎样实现。下面是一个例子

    constructor(props) {
      super(props);
      this.state = {
        color: props.initialColor
      };
    }  

    要谨慎使用这种模式(使用props初始化state),这可能会导致bug,而且你可能会需要实现componentWillReceiveProps(nextProps),来保持props和state同步。相比于同步props到state中,lift state up(将state提升到共同的父组件中)会是更好的办法。

    3. componentWillMount()

    componentWillMount()函数在mounting即将发生前立即调用。在render()函数之前调用,因此在这个方法中设置state将不会触发re-rendering。Avoid introducing any side-effects or subscriptions in this method.

    这是唯一在服务器渲染中调用的生命周期钩子函数。一般来说,建议使用constructor()代替。

    4. componentDidMount()

    componnetDidMount()函数在组件mount完后立即调用。在这里可以访问DOM元素。如果你需要从远程端点加载数据,这是实例化网络请求的好地方。在这里设置state会触发re-render。如果想和其它 JavaScript 框架集成,使用 setTimeout 或者 setInterval 来设置定时器,或者发送 AJAX 请求,可以在该方法中执行这些操作。

    class Box extends React.Component{
      constructor(props){
        super(props);
        this.state={windowWidth:window.innerWidth};
        this.handleResize=this.handleResize.bind(this);
      }
      handleResize(e) {
        this.setState({windowWidth: window.innerWidth});
      }
      componentDidMount() {
        window.addEventListener('resize', this.handleResize);
      }
      componentWillUnmount() {
        window.removeEventListener('resize', this.handleResize);
      }
      render(){
        return <div>Current window  {this.state.windowWidth}</div>;
      }
    }
    ReactDOM.render(<Box />, document.body);

    componentDidMount 会在 component 渲染完成且已经有了 DOM 结构的时候被调用。通常情况下,你可以在这绑定普通的 DOM 事件。

    注意,事件的回调被绑定在了 react 组件上,而不是原始的元素上。React 通过一个 autobinding 过程自动将方法绑定到当前的组件实例上。

    5.componentWillReceiveProps(nextProps)

    当一个装载好的组件接收到新的props时会调用这个函数。如果props变化时你需要更新state(比如重置它),你可以对比props和nextProps,然后执行this.setState()。

    注意props没有发生变化时,React也可能会调用这个函数,因此一定要对比props和nextProps来确定是否发生变化。当你的父组件导致组件re-render时,会调用这个函数。

    如果你只是调用this.setState()并不会触发componentWillReceiveProps.

    对于 state,没有相似的方法: componentWillReceiveState。将要传进来的 prop 可能会引起 state 改变,反之则不然。如果需要在 state 改变的时候执行一些操作,请使用 componentWillUpdate

    6. shouldComponentUpdate(nextProps, nextState)

     使用shouldComponentUpdate函数来使得React知道是否现在state或者props的改变影响了组件的输出。默认情况下是,每次state改变都会re-render,而且大多数情况下都应该使用默认行为。

    在render之前当接收到新的props或者state时执行shouldComponentUpdate()。函数返回值默认为true。首次render或者当使用了forceUpdate()时这个方法不会执行该函数。当子组件的state改变时,即使返回false也不会阻止子组件re-render。

    目前,如果shouldComponentUpdate()返回false,那么componentWillUpdate(),render(),componentDidUpdate()都不会执行。但是在未来,React可能只是将shouldComponentUpdate()作为一个线索而不是一个严格的指令,并且返回false可能仍然使得组件re-render。

    你也可以使得你的组件继承React.PureComponent,这样它的shouldComponentUpdate()只会在浅层次比较prop和state。如果你想要自己写这个函数,可以比较this.props和nextProps,nextState和this.state,如果需要跳过这次更新可以返回false。

    7.componentWillUpdate(nextProps,nextState)

    render之前,当接受到新的props和state时,componentWillUpdate()立即执行。此时可以在更新前做一些准备。首次render时这个函数不会执行。注意在这个函数里面不能调用this.setState()。如果你需要更新state来适应prop的改变,可以在componentWillReceiveProps()中进行。另外,当shouldComponentUpdate()返回false时,这个函数不会执行。

    8.componentDidUpdate(prevProps,prevState)

     更新完成后立即执行componentDidUpdate函数。首次render时不会执行。

    使用该方法可以在组件更新之后操作 DOM 元素。这也是一个执行网络请求的好地方只要你对比了当前props和之前的props(比如当props没有改变时,不需要发起网络请求)。

    注意:如果shouldComponentUpdate返回false,则componentDidUpdate将不会执行。

    9.componentWillUnmount

    组件unmounted并且destroy前执行componentWillUnmount.在这里可以做一些必要的清理,比如取消timer,取消网络请求,或者清理componentDidMount阶段创建的DOM元素。

    forceUpdate(callback)

    默认情况下,当你的组件state或者props改变了,你的组件就会re-render。如果你的render()方法取决于一些其他的数据,你可以通过调用forceUpdate告诉React组件需要re-render.

    调用forceUpdate()将导致组件执行render(),跳过shouldComponentUpdate()。这将会触发子组件的生命周期函数,包括每个子组件的shouldComponentUpdate(). React will still only update the DOM if the markup changes.

    通常你应该尽量避免使用forceUpdate(),只在render中读取this.props和this.state

    二、 函数的使用

    1. Mounting

     var count=0;
        class HelloWorld extends React.Component{
            constructor(props){
                super(props);
                this.state={myCount:count++,ready:false};
            }
            componentWillMount(){
                this.setState({ready:true});
            }
            render(){
                return <p ref="childp">Hello {this.props.name?this.props.name:"World"}<br/>
                {" "+this.state.ready}<br/>count:{this.state.myCount}</p>
            }
            componentDidMount(){
                var para=document.createElement("p");
                para.appendChild(document.createTextNode("surprise"));
                React.findDOMNode(this).appendChild(para);  // React.findDOMNode(this) 找到组件的真实DOM节点
            }
        }
        HelloWorld.defaultProps={name:"james"};
        React.render(<div><HelloWorld/><HelloWorld/></div>,document.body);

    2. Updating

      class HelloWorld extends React.Component{
            componentWillReceiveProps(){}
            shouldComponentUpdate(){
                return true;
            }
            componentWillUpdate(){
    
            }
            render(){
                return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
            }
            componentDidUpdate:function(){}
        }
        class HelloUniverse extends React.Component{
            constructor(props){
                super(props);
                this.hadleChange=this.handleChange.bind(this);
    this.state={name:''}; } handleChange(event){
    this.setState({name:event.target.value}); } render(){ return <div> <HelloWorld name={this.state.name}/> <br/> <input type="text" onChange={this.handleChange}/> </div> } } React.render(<HelloUniverse/>,document.body);

     实现像AngularJS一样的双向数据绑定效果

     

    class HelloWorld extends React.Component{
        componentWillReceiveProps(property){ // 收到父组件传入的name属性,传入的是个object
            console.log("componentWillReceiveProps:1==>"+property.name);
            property.name="hehe"+property.name;  // 获得父组件传入的name属性后,先进行处理再传给组件使用
        }
        shouldComponentUpdate(){ // 不常用
            console.log("shouldComponentUpdate:2");
            return true;
        }
        componentWillUpdate(){ // 不常用
            console.log("componentWillUpdate:3");
        },
        render(){
            console.log("render:4");
            return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
        }
        componentDidUpdate(){
            console.log("componentDidUpdate:5");
                    var para=document.createElement("p");
                    para.appendChild(document.createTextNode("surprise"));
                    React.findDOMNode(this).appendChild(para);
        }
    }

     执行顺序

     

    3. Unmounting

    1)在render中将之前有的组件去掉,反映到页面上就是把他删除掉就是unmount掉

    var HelloWorld=React.createClass({
            render:function(){
                return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
            },
            componentWillUnmount:function(){
                console.log("componentWillUnmount");
            }
        });
        class HelloUniverse extends React.Component{
            constructor(props){
                 super(props);
                 this.state={name:''};
                 this.handleChange=this.handleChange.bind(this);
            }
            handleChange(event){
                this.setState({name:event.target.value});
            }
            render(){
                if(this.state.name=="123"){
                    return <div>123</div>;
                }
                return <div>
                        <HelloWorld name={this.state.name}/>
                        <br/>
                        <input type="text" onChange={this.handleChange}/>
                       </div>
            }
        }
        React.render(<HelloUniverse/>,document.body);
    ====>  

     

    2)使用react提供的函数React.unmountComponentAtNode(装载的节点),这个装载的节点就是React.render(组件,装载的节点)中的第二个参数

    <script type="text/jsx">
        var HelloWorld=React.createClass({
            render:function(){
                return <p>Hello,{this.props.name?this.props.name:"World"}</p>;
            },
            componentWillUnmount:function(){
                console.log("componentWillUnmount");
            }
        });
        var HelloUniverse=React.createClass({
            getInitialState:function(){
                return {name:''};
            },
            handleChange:function(event){
                if(event.target.value=="123"){
                   React.unmountComponentAtNode(document.body);
                   return;
                }
                 this.setState({name:event.target.value});
            },
            render:function(){
                return <div>
                    <HelloWorld name={this.state.name}/>
                    <br/>
                    <input type="text" onChange={this.handleChange}/>
                    </div>
            }
        });
        React.render(<HelloUniverse/>,document.body);
    </script>
     ===>  

     直接将HelloUniverse卸载了

  • 相关阅读:
    vue-cli搭建项目结构及引用bootstrap
    万年历案例
    art-template模板渲染及其过滤器
    字符串中全角半角之间的转换
    大话主席(superslide和 touchslide)插件的使用
    JS中对URL进行转码与解码
    animate.css引入实现动画效果
    [MySQL]group by 与 having 结合函数 的统计技巧
    [HTTP] 基本认证的工作流程
    [HTTP]Etag的工作流程
  • 原文地址:https://www.cnblogs.com/YangqinCao/p/6035544.html
Copyright © 2020-2023  润新知