React 学习笔记3 生命周期 受控/非受控组件

发布于:2025-09-02 ⋅ 阅读:(20) ⋅ 点赞:(0)

事件

在React,事件绑定通过on事件名(事件名首字母大写)的形式来进行绑定。

React中使用的事件,是React重新封装的自定义事件,并不是原生DOM事件,这是为了更好的兼容性考虑。

React的事件是通过事件委托的方式处理的,也就是说,DOM上绑定的事件会被委托给最外层的DOM。通过这种方式提升效率。事件回调函数具有参数event,因此也可以使用event.target获得事件对应的DOM。

受控组件

数据存储到state中,需要数据时从state中取出数据。这种思想类似于vue中的双向数据绑定。

        <script type="text/babel">

        class Cat extends React.Component {
            handleSubmit = (event)=>{
                event.preventDefault()
                alert(`username:${this.state.username} password:${this.state.password}`)
            }

            state = {
                username:'',
                password:'',
            }

            saveName = (event)=>{
                this.setState({username:event.target.value})
            }

            savePassword = (event)=>{
                this.setState({password:event.target.value})
            }

            render(){
                return (
                    <div>
                        <form onSubmit={this.handleSubmit}>
                            name:<input onChange={this.saveName}/>
                            password:<input onChange={this.savePassword}/>
                            <button>login</button>    
                        </form>
                    </div>
                )
            }
        }
        
        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

非受控组件

通过各种方法,需要数据的时候,现获得对应DOM中存储的数据。

        <script type="text/babel">

        class Cat extends React.Component {
            handleSubmit = (event)=>{
                event.preventDefault()
                alert(`username:${this.username.value} password:${this.password.value}`)
            }

            render(){
                return (
                    <div>
                        <form onSubmit={this.handleSubmit}>
                            name:<input ref = {curr => this.username = curr}/>
                            password:<input ref = {curr => this.password = curr}/>
                            <button>login</button>    
                        </form>
                    </div>
                )
            }
        }
        
        ReactDOM.render(<Cat />, document.getElementById('demo'))
    </script>

高阶函数

在往state中存放数据时,可以不用给每个属性都设置自己的方法,可以只定义一个通用的属性设置方法,然后把需要保存的数据key以参数的形式传递给函数。

但在React中,无法给绑定的函数设置参数,因为React会把函数的返回值作为事件回调。

为了解决这种情况,可以让事件绑定的函数返回函数,也就是使用高阶函数(高阶函数,也就是函数参数,或函数返回值是一个函数的函数。promise、setTimeout、数组的map 等都是高阶函数)

<body>
    <div id="demo">
 
    </div>
    <script src="https://unpkg.com/react@17/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
 
    <!-- Don't use this in production: -->
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>

    <script src="https://unpkg.com/prop-types@15.6/prop-types.js"></script>
 
    <script type="text/babel">
        class Cat extends React.Component{
          state = {
            password:"",
            username:"",
          }

          saveData = (dataType)=>{
            return (event)=>{
              this.setState({
                [dataType] : event.target.value
              })
            }
          }

          handleSubmit = (event)=>{
            event.preventDefault()
            alert(`username:${this.state.username} password:${this.state.password}`)
          }

          render(){
            return (
              <form onSubmit={this.handleSubmit}>
                username:<input onChange={this.saveData('username')} type="text" name="username"></input>  
                password:<input onChange={this.saveData('password')} type="password" name="username"></input>
                <button>login</button>
              </form>
            )
          }

        }
        ReactDOM.render(<Cat/>,document.getElementById('demo'));
    </script>
</body>

函数柯里化

函数柯里化:函数调用时返回函数,可以多次接收参数,最后统一处理的一种函数编码形式。

<body>
    <script>
        function sum(a){
            return (b)=>{
                return (c)=>{
                    return a+b+c
                }
            }
        }

        console.log( sum(3)(5)(6) )
    </script>
</body>

实际上,上面的saveData函数也应用了柯里化技术,只不过函数层次不深。

生命周期

在组件创建和销毁的过程中,在不同的时机,不同的函数会被自动调用。这些函数叫做生命周期回调函数,也叫生命周期钩子函数。或者生命周期钩子。

初步认识生命周期

render函数在组件初始化,及state更新时调用。

组件挂载完毕后,componentDidMount()函数会被执行一次。这个函数会被通过实例.调用,内部的this指向实例。

通过ReactDOM.unmountComponentAtNode(需要卸载的组件所在的DOM),可以卸载当前组件。

在组件被将要被卸载之前,componentWillUnmont()函数会被调用。

一个小例子:组件挂载时触发定时器,修改页面显示数据的透明度,点击卸载按钮,关闭定时器并卸载组件。

<script type="text/babel">
        class Cat extends React.Component{
          state = {
            opacity:1,
          }

          componentDidMount(){
            this.timer = setInterval(
              ()=>{
                let nowOpcity = this.state.opacity
                nowOpcity -= 0.1
                if(nowOpcity <= 0) nowOpcity = 1
                this.setState({
                  opacity:nowOpcity
                })
              },200
            )
          }       
          
          componentWillUnmount(){
            clear(this.timer)
          }

          unmountCom = ()=>{
            ReactDOM.unmountComponentAtNode(document.getElementById('demo'))
          }

          render(){
            return (
              <div>
                <h2 style={{opacity:this.state.opacity}} > hello React </h2>
                <button onClick={this.unmountCom}>unmount</button>
              </div>
            )
          }
        }
        
        ReactDOM.render(<Cat/>,document.getElementById('demo'))
    </script>

生命周期流程

旧生命周期

在React中,生命周期有两个版本,在旧版本(适用于16.3及之前的React)

在组件刚开始挂载(初始化/挂载时)时,首先被调用的是构造器constructor,然后是componentWillMount,它在组件将要挂载时调用。然后是render。render之后组件挂载完毕,触发 (常用,一般在这个函数中写一些初始化操作、开启定时器、发送网络请求、订阅消息等)。

当组件发生更新时,有三种情况,第一种情况是通过setState更新状态,setState调用时,会触发shouldComponentUpdate钩子(组件是否应该被更新钩子),在钩子内部会判断是否需要更新,需要这个钩子返回true,会继续触发后续的钩子,如果返回false,则更新流程结束。这个钩子相当于一个阀门,在没有主动定义时,默认返回true。如果钩子返回true,后续会触发componentWillUpdate钩子,它在组件将要更新前调用。然后触发render,然后是 。

第二种情况是通过forceUpdate函数更新状态,forceUpdate函数用于强制更新,强制更新时,直接触发componentWillUpdate钩子,走render、componentDidUpdate。就算组件没有状态更新,也会调用更新钩子。forceUpdate的使用方法,是通过this.forceUpdate()触发。

第三种情况,是父组件render被触发时。在A组件中写了另一个组件标签B,A就是父组件,B就是子组件。当父组件A的render被触发时,子组件就会触发componentWillReceiveProps钩子,这个钩子在子组件将要接收props时调用,而且是新props时调用,也就是说,当父组件传递给子组件的props发生更新时,才会触发这个钩子,父组件初始给子组件传递Props,这种情况不会触发钩子,只有更新时才触发。这个钩子有一个参数props,接收到的是子组件获得的props对象。然后是shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate流程。

componentDidUpdate钩子具有三个参数,之前的props和state,也就是更新前的props和state。以及快照(快照是getSnapshotBeforeUpdate的返回值)。

最后,组件卸载之前会调用componentWillUnmount(常用,一般在这个函数中写一些收尾的逻辑,关闭定时器、取消订阅等)。

    <script type="text/babel">
        class Cat extends React.Component{
          state = {
            a:1,
          }

          change = ()=>{
            console.log('-------------------')
            this.setState({a:2})
          }

          unmountCom = ()=>{
            console.log('-------------------')
            ReactDOM.unmountComponentAtNode(document.getElementById('demo'))
          }

          constructor(props){
            super(props)
            console.log('------constructor')
          }

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

          render(){
            console.log('------render')
             return (
              <div>
                <h2  > {this.state.a} </h2>
                <button onClick={this.change}>change</button>
                <button onClick={this.unmountCom}>unmount</button>
              </div>
            )
          }

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

          shouldComponentUpdate(){
            console.log('------shouldComponentUpdate')
            return true
          }
          
          componentWillUpdate(){
            console.log('------componentWillUpdate')
          }

          componentDidUpdate(){
            console.log('------componentDidUpdate')
          }

          componentWillUnmount(){
            console.log('------componentWillUnmount')
          }
        }
        
        ReactDOM.render(<Cat/>,document.getElementById('demo'))
    </script>

新生命周期

React还有一个新版本的生命周期(适用于16.4及之后的React):

较新版本的React中使用的是新生命周期,在新版本中,WillUpdate、WillMount和WillReceiveProps需要在前面加UNSAFE_,变成UNSAFE_componentWillUpdate、UNSAFE_componentWillMount、UNSAFE_componentWillReceiveProps。UNSAFE不是指安全性,而是指这三个钩子在后续版本中可能会出现BUG。

除此之外,新生命周期多了getDerivedStateFromProps钩子和getSnapshotBeforeUpdate。

新生命周期中,挂载时,在constructor后,触发getDerivedStateFromProps,然后是render、componentDidMount。getDerivedStateFromProps钩子在挂载和更新的时候都会调用,它的英文单词意思是“从props中得到派生状态”。这个函数并不是给实例使用的,它应该是静态方法,在使用时应该加上静态方法static getDerivedStateFromProps,参数是props和state,并且需要返回state object或者null。如果该方法返回状态对象,state中的数据会以getDerivedStateFromProps中的返回值为准,且这个状态值无法更新,在任何时候都是返回的数据。一般在getDerivedStateFromProps中把props作为state返回,但它的用途很罕见,一般不会使用getDerivedStateFromProps,因为getDerivedStateFromProps会造成代码冗余、组件难以维护。但如果任何时候,state的值都取决于props,并且这个值不会修改更新,就可以使用getDerivedStateFromProps。

更新时,不论是哪种情况触发的更新,都会先触发getDerivedStateFromProps,如果是setState、父组件render被触发的情况,之后会走入shouldComponentUpdate,foreceUpdate会略过这个钩子,然后三者都会走向render、getSnapshotBeforeUpdate、componentDidUpdate。getSnapshotBeforeUpdate在render和DidUpdate之间触发,英文单词的含义是“在更新之前获取快照”。getSnapshotBeforeUpdate需要返回一个快照(任何值都可以作为快照),或者null。返回的快照在页面更新之前调用,使得组件能在DOM更新之前获取一些数据,它返回的快照会被传递给componentDidUpdate。getSnapshotBeforeUpdate在实际开发中也并不常用。

卸载时,触发componentWillUnmount。

    <script type="text/babel">
        class Cat extends React.Component{
          state = {
            a:1,
          }

          change = ()=>{
            console.log('-------------------')
            this.setState({a:2})
          }

          unmountCom = ()=>{
            console.log('-------------------')
            ReactDOM.unmountComponentAtNode(document.getElementById('demo'))
          }

          constructor(props){
            super(props)
            console.log('------constructor')
          }

          static getDerivedStateFromProps(props,state){
            console.log(props,state)
            return props
          }


          render(){
            console.log('------render')
             return (
              <div>
                <h2  > {this.state.color} </h2>
                <button onClick={this.change}>change</button>
                <button onClick={this.unmountCom}>unmount</button>
              </div>
            )
          }

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

          


          shouldComponentUpdate(){
            console.log('------shouldComponentUpdate')
            return true
          }
          
          
          getSnapshotBeforeUpdate(){
            return '1'
          }

          componentDidUpdate(preProps,preState,snapshot){
            console.log(preProps,preState,snapshot)
            console.log('------componentDidUpdate')
          }

          componentWillUnmount(){
            console.log('------componentWillUnmount')
          }
        }
        
        ReactDOM.render(<Cat color="orange"/>,document.getElementById('demo'))
    </script>


网站公告

今日签到

点亮在社区的每一天
去签到