react笔记3---state和生命周期

state

state允许React组件在不违反props规则的情况下,根据用户操作,网络影响,或者其他随便什么东西,来动态改变其输出,类似于vue中的data

state的定义

constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

state不能直接修改,需要调用this.setState()

// 错误  这样将不会重新渲染一个组件:
this.state.comment = 'Hello';

// 正确    用 setState() 代替:
this.setState({comment: 'Hello'});

唯一可以分配 this.state 的地方是构造函数。

state(状态)更新可能是异步的

使用回调函数的形式实现异步操作

// 错误
this.setState({
  counter: this.state.counter + this.props.increment,
});


// 正确
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

使用另一种 setState() 的形式,它接受一个函数而不是一个对象。

这个函数将接收前一个状态作为第一个参数,应用更新时的 props 作为第二个参数:

state(状态)更新会被合并

你的状态可能包含几个独立的变量

调用this.setState()修改单个的状态不会影响其他的状态,只会合并当前修改这一个状态


hook函数请参考链接

 https://baike.baidu.com/item/%E9%92%A9%E5%AD%90%E5%87%BD%E6%95%B0?fr=aladdin


react组件的三个状态

1、mount

react Components被render解析生成对应的dom节点被插入浏览器的dom结构一个的过程

在浏览器看到组件元素从无到有的过程

2、update

一个mounte的react Component被重新render的过程,但是在这个过程中,dom结构并不一定会发生改变

在react中,状态的改变会触发update的hook函数

3、unmount

一个mount的react Component对应的dom节点被从dom中移除的一个过程

react针对以上三种状态都封装了hook函数


生命周期钩子

mounting

如下这些方法在组件实例被创建和被插入到dom中时被调用

1.constructor()

constructor初始化state的好地方,如果我们不需要初始化state,也不需要bind任何方法,那么在我们的组件中不需要实现constructor函数。

constructor在组件被mounted之前调用,我们的组件继承自React.Component,constructor函数中我们在其他操作前应该先调用super(props),否则this.props将会是undefined

注意下面的情况,很容易产生bug,我们通常的做法是提升state到父组件,而不是使劲的同步state和props

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

此方法在mounting之前被立即调用,它在render()之前调用,因此在此方法中setState不会触发重新渲染。此方法是服务器渲染中调用的唯一生命周期钩子,通常我们建议使用constructor()。

3.render()

render()方法是react组件必须的,它检查this.props和this.state并且返回一个React元素,我们也可以返回null和false,代表我们不想有任何的渲染

render()方法是一个纯方法,即它不会修改组件的state,在每一次调用是返回同样的结果。它不直接和浏览器交互,如果我们想要交互,应该在componentDidMount()或者其他的生命周期函数里面

4.componentDIDMount()

此方法在组件被mounted之后立即被调用,初始化dom节点应该在此方法中,如需要从远端健在数据,这里是实例化网络请求的好地方,此方法中setState会触发组件重新渲染


Updating

props和state的改变产生更新。在重新渲染组建时,如下的方法被调用

1.componentWillReceiveProps()

一个已经mounted的组件接收一个新的props之前componentWillReceiveProps()被调用,如果我们需要更新state来响应prop的更改,我们可以在此方法中比较this.props和nextProps并使用this.setState来更改state。

注意,即使props没有改变,React也可以调用这个方法,因此如果你只想处理改变,请确保比较当前值和下一个值。当父组件导致你的组件重新渲染时,可能会发生这种情况。

React在组件mounting期间不会调用此方法,只有在一些组件的props可能被更新的时候才会调用。调用this.setState通常不会触发componentWillReceiveProps。

2.shouldComponentUpdate()

使用此方法让React知道组件的输出是否不受当前state或props更改的影响。默认行为是在每次state更改时重新渲染组件,在大多数情况下,我们应该默认改行为。

当接收到新的props或state时,shouldComponentUpdate()在渲染之前被调用。默认返回true,对于初始渲染或使用forceUpdate()时,不调用此方法。返回false不会阻止子组件的state更改时,该子组件重新渲染。

如果shouldComponentUpdate()返回false,那么componentWillUpdate(),render()和componentDidUpdate()将不会被调用。在将来,React可能将shouldComponentUpdate()作为提示而不是strict指令,返回仍然可能导致组件重新渲染。

3.componentWillUpdate()

当接收新的props或state时,componentWillUpdate()在组件渲染之前被立即调用。使用此函数作为在更新发生之前执行准备的机会。初始渲染不会调用此方法。

注意:这里不能调用this.setState()(如果调用会怎么样?好奇心很重呀,试了一下,会产生死循环,一直更新。

如果我们需要更新state以响应props的更改,我们应该使用componentWillReceiveProps()

4.render()

render()方法是react组件必须的,它检查this.props和this.state并且返回一个React元素,我们也可以返回null或false,代表我们不想有任何的渲染。

render()方法应该是一个纯方法,即它不会修改组件的state,在每一次调用时返回同样的结果。它不直接和浏览器交互,如果我们想要交互,应该在componentDidMount()或者其他的生命周期函数里面。

5.componentDidUpdate()

此函数在更新后立即被调用。初始渲染不调用此方法。

当组件已经更新时,使用此操作作为DOM操作的机会。这也是一个好的地方做网络请求,只要你比较当前的props和以前的props(例如:如果props没有改变,可能不需要网络请求)。


Unmounting

1.componentWillUnmount()

当从dom中移除组件时,这个方法会被调用

此函数在组件被卸载和销毁之前被立即调用。在此方法中执行一些必要的清理。例如清除计时器,取消网络请求或者清理在componentDidMount中创建的任何DOM元素。


演示

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Hello World</title>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script crossorigin src="https://unpkg.com/[email protected]/babel.min.js"></script>
    </head>

    <body>
        <div id="root"></div>
    </body>

</html>
<script type="text/babel">

    // 创建一个类组件
    class LifeCycle extends React.Component {

        // 1. mount阶段
        constructor(props) {
            super(props);
            this.state = {name: 'wlt'};
            this.changeState = this.changeState.bind(this);
            console.log('constructor');
        }

        componentWillMount() {
            console.log('componentWillMount');
        }

        render() {
            console.log('render');
            return (
                <div className="life-cycle-bg">
                    <p>Hello {this.props.value}</p>
                    <p>Hello {this.state.name}</p>
                    <button onClick={this.changeState}>改变lifeCycle的state</button>
                </div>
            );
        }

        componentDidMount() {
            console.log('componentDidMount');
        }

        // 2. update阶段
        componentWillReceiveProps(nextProps) { // 只作用于属性的变化,不作用于状态的变化
            console.log('componentWillReceiveProps');
        }

        shouldComponentUpdate(nextProps, nextState) {
            console.log('shouldComponentUpdate');
            return true;
        }

        componentWillUpdate(nextProps, nextState) {
            console.log('componentWillUpdate');
        }

        // ---- render会重新执行

        componentDidUpdate(prevProps, prevState) {
            console.log('componentDidUpdate');
        }

        // 3. Unmount阶段
        componentWillUnmount(prevProps, prevState) {
            console.log('componentWillUnmount');
        }

        // 修改状态
        changeState() {
            this.setState({name: 'sxm'});
        }
    }

    class ParentLifeCycle extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                value: 'World',
                destroyed: false,
                rerender: false
            };
            this.handleChange = this.handleChange.bind(this);
            this.destroyComponent = this.destroyComponent.bind(this);
            this.handleRerender = this.handleRerender.bind(this);
        }

        handleChange() {
            this.setState((prevState, props) => ({
                value: prevState.value + ' wlt'
            }));
        }

        handleRerender() {
            this.setState({rerender: true});
        }

        destroyComponent() {
            this.setState({destroyed: true});
        }

        render() {
            if(this.state.destroyed) return null;
            return (
                <div className="parent-life-cycle-bg">
                    <p>
                        <button onClick={this.handleChange}>改变LifeCyle的props</button>
                        <button onClick={this.handleRerender}>父组件重新渲染,子组件re-render</button>
                        <button onClick={this.destroyComponent}>删除组件</button>
                    </p>
                    <LifeCycle value={this.state.value}/>
                </div>
            );
        }
    }

    ReactDOM.render(
        <ParentLifeCycle />, document.getElementById('root')
    );
</script>


例子

实现一个基本的定时器功能

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Hello World</title>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script crossorigin src="https://unpkg.com/[email protected]/babel.min.js"></script>
    </head>

    <body>
        <div id="root"></div>
    </body>

</html>
<script type="text/babel">


    // 创建一个组件 -- 使用函数式组件
    function Clock(){
        // 创建元素
        const element = (
            <div>
                <h1>hello world!</h1>
                <h2>It is {new Date().toLocaleTimeString()}</h2>
            </div>
        )

        // 使用render函数渲染已经创建的元素
        ReactDOM.render(
            element, 
            document.getElementById('root') 
        );
    }

    setInterval(Clock,1000)


</script>

存在的问题

完成定时器的功能,使用组件化的形式进行封装的时候,在进行组件调用的时候应该能够直接通过 就能完成一个定时器,而不用再配合外部的js操作

这样做的话能够实现相关的功能,但是不利于复用

改写
使用组件的形式

<!DOCTYPE html>
<html>

    <head>
        <meta charset="UTF-8" />
        <title>Hello World</title>
        <script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
        <script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
        <script crossorigin src="https://unpkg.com/[email protected]/babel.min.js"></script>
    </head>

    <body>
        <div id="root"></div>

    </body>

</html>

<!--

    在组件挂载和卸载的时候,会执行对应的生命周期钩子函数


-->

<script type="text/babel">

    // 使用类的形式对组件进行改写之后,我们可以向类中添加一些内容
    class Clock extends React.Component{ 

        constructor(props){
            // 类组件应始终使用 props 调用基础构造函数。
            super(props)
            // 使用状态进行改写
            this.state = {
                date:new Date()
            }
        }


        // 组件挂载的生命周期钩子函数
        componentDidMount(){
            console.log("组件挂载了")

            this.timer = setInterval(()=>{
                // 使用this.setState() 更新本地的状态
                this.setState({
                    date:new Date()
                })
            },1000)

        }

        // 组件卸载时会执行的生命周期钩子函数
        componentWillUnmount(){
            console.log("组件卸载了")
            clearInterval(this.timer)
        }

        render(){ 
            return (
                <div>
                    <h1>hello world!</h1>
                    <h2>It is {this.state.date.toLocaleTimeString()}</h2>
                </div>
            ) 
        } 
    } 


        // 使用render函数渲染已经创建的组件 -----  三个组件互补影响
        ReactDOM.render(
            <div>
                <Clock></Clock>
                <Clock></Clock>
                <Clock></Clock>
            </div>,
            document.getElementById('root') 
        );
</script>

猜你喜欢

转载自blog.csdn.net/scathachservant/article/details/80600791