当前位置: 首页 > news >正文

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

事件

在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.opacitynowOpcity -= 0.1if(nowOpcity <= 0) nowOpcity = 1this.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>

http://www.dtcms.com/a/361188.html

相关文章:

  • 阿里云代理商:轻量应用服务是什么?怎么用轻量应用服务器搭建个人博客?
  • 大模型落地:从微调到部署的全景式实战指南
  • MFC应用防止多开
  • Prometheus Alertmanager 告警组件学习
  • Linux 正则表达式与grep命令
  • 车载卫星通信:让自动驾驶“永不掉线”?
  • Kafka面试精讲 Day 4:Consumer消费者模型与消费组
  • 指针数组与数组指针的区别
  • 【第十一章】Python 队列全方位解析:从基础到实战
  • 鸿蒙NEXT表单选择组件详解:Radio与Checkbox的使用指南
  • 绝了!极空间搭配视频智语,生产力拉满,多平台视频摘要一键搞定
  • browsermobproxy + selenium 获取接口json
  • PLC操作
  • AI + 机器人:当大语言模型赋予机械 “思考能力”,未来工厂将迎来怎样变革?
  • 森赛睿视觉AI:大模型加持,分类更智能
  • 宋红康 JVM 笔记 Day09|方法区
  • 虚拟化技术是什么?电脑Bios中的虚拟化技术怎么开启
  • 【2025ICCV】Vision Transformers 最新研究成果
  • NetCoreKevin-DDD-微服务-WebApi-AI智能体、AISK集成、MCP协议服务、SignalR、Quartz 框架-14-数据模型与持久化
  • YOLO 目标检测:YOLOv4数据增强、CIoU Loss、网络结构、CSP、SPPNet、FPN和PAN
  • 架构选型:为何用对象存储替代HDFS构建现代数据湖
  • Linux之Shell编程(四)函数、数组、正则
  • 小土堆目标检测笔记
  • 【开题答辩全过程】以 基于Spring Boot的房屋租赁系统的设计与实现为例,包含答辩的问题和答案
  • go语言面试之Goroutine 数量控制, GC回收 和任务调度
  • 【Qwen】Qwen3-30B-A3B 模型性能评估指南 + API KEY介绍
  • DAY02:【DL 第一弹】pytorch
  • JS闭包讲解
  • 在 Halo 中导入 Markdown 和 Word 文档
  • openEuler2403编译安装Nginx