React类组件实战知识点
React02-项目搭建
一、React介绍
React这个项目是facebook开源出来的一个优秀前端库。
官网将React称为库(jquery库),React默认提供的只是前端的页面更新方案。并没有提供完整的前端所有核心内容。比如路由、状态机。这些都需要我们自己搭建。
在2013年5月开源出来。
提出了很多概念在当时都是比较前卫的思想。
组件化开发、虚拟dom加载。
React开发有两个分支:
- React-DOM:这是一个包。这个分支解决我们React开发WEB业务
- React-Native:专门用于开发移动端。只能开发android和ios
优势:
- 组件化开发、虚拟dom设计,对应项目来说性能有保证。
- 技术成熟、社区配置完善。适用于中大型web项目
- facebook团队维护,版本更新、担心后续维护
- 目前来说React在公司里面,适用于中大型项目。大公司用的比较多
二、搭建项目
目前React官方基于webpack封装了脚手架。
你们可以基于脚手架工具。快速搭建一个项目。
(1)创建项目
npx create-react-app 项目名字
脚手架工具的文档:
这种方式来创建项目,本地无需安装脚手架。就可以直接创建项目
临时在本地安装一个脚手架工具,创建项目,创建完成后,删除脚手架
在本地安装好脚手架,直接用本地脚手架搭建项目。这种方式我们不推荐。
npm i create-react-app -g
create-react-app 项目名字
创建项目,项目名字不能出现大写字母,不能中文
(2)启动项目
安装yarn包管理器。默认提示你采用yarn启动项目
yarn start
npm来启动项目
npm run start
(3)改造项目
src目录下面只留
- index.js:这个文件项目启动文件。类似于Vue中main.js以后需要全局挂载内容都可以在里面写
- App.js:后缀是js,其中这个是React组件。里面代码不是原生js代码。会在index默认加载App.js文件
剩下的文件夹需要大家自己创建
component:存放组件
utils:存放开发自己封装工具
assets:静态资源目录
libs:存放第三方包 sdk
apis:存放异步请求
三、组件的介绍
(1)概念介绍
组件化思想:针对页面UI部分,进行了页面的拆分。将公共的页面模板拆分,最后在自己组合在一起。
模块化思想:针对JS脚本设计,将代码进行模块拆分。减少当前脚本体积,提高复用性。比如很多的工具提取出来。
<html>
<head>
<script src="./a.js"></script>
<script src="./b.js"></script>
</head>
</html>
a.js
var m = 10
b.js
var m = 20
最早解决思路
采用IIFE来解决问题。
a.js
(function(){
var m = 10
})()
b.js
(function(){
var m = 20
})()
requirejs seajs等等。第三方开发的模块中代码。
ES6这个版本,官方也推出了模块化思想
import xxx from "./"
export {}
export default {}
工程化思想:以前我们项目在没有脚手架搭建的时候,都是自己下载包。自己来进行代码压缩混淆。
基于webpack或者vite这种打包工具。你们直接依赖这些工具完成前端项目构建,打包、部署
(2)组件创建
React中组件有两种
- 类组件开发:采用的面向对象开发思想。
- 函数组件开发:基于函数来设计组件。所有业务都是基于函数来开展
在目前项目开发过程中,两种组件都会接触到。
在React中我们组件后缀有两种格式,js
在里面写jsx代码,jsx
后缀支持的一种格式
安装插件
创建组件的时候
rcc
:创建一个类组件的模板
rfc
:创建一个函数组件的的模块
类组件的代码
import React, { Component } from 'react'
/**
* 在React中当写一个类,继承Component,当前这个类是一个组件
* Component:React提供的组件
*/
class Header extends Component {
/**
* render渲染函数,我们当前这个组件要显示模板,在render里面
* JSX代码
* @returns
*/
render() {
return (
<div>Header</div>
)
}
}
export default Header
React设计思想:将JS+HTML模板统一放在一个文件中,写完了代码后。JS 和HTML分开解析。最后html+css+js
原生JS代码
const array = [1,2,3]
let temp = ""
for(var i=0;i<array.length;i++){
temp += `<li>${array[i]}</li>`
}
obody.innerHTML = temp
函数组件的代码
import React from 'react'
function Content() {
return (
<div>Content</div>
)
}
export default Content
我们先学习类组件,后面再学习函数组件
(3)JSX概念
组件本身设计的一种模块,可以将代码抽取出来。再React中就是采用JSX来作为组件的语法
JSX = JavaScript + XML
JavaScript就是基于ES5\ES6引入过来规范。
XML:标签语言,表示组件中所有标签模板。代码解析的时候。XML标签就是转化HTML标记了。
接下来你再组件中写的所有代码都是JSX代码。浏览器无法直接解析。
在启动项目的时候
yarn start
webpack就会开始打包项目。babel这个插件,将jsx代码转化为浏览器能是被的js代码。最后才浏览器运行
四、组件的页面
(1)组件中类名
<div className="">
每个组件都要求,必须提供根标签。否则报错
<React.Fragment></React.Fragment>
使用这个来作为根标签,最终页面不会渲染出来。解决报错问题
<></>
也是代表虚拟标签,最终并不会解析出来。
(2)组件中引入图片
import React, { Component } from 'react'
import logo from "../assets/images/logo-250px.645f24b5.png"
const logo2 = require("../assets/images/logo-250px.645f24b5.png")
/**
* 在React中当写一个类,继承Component,当前这个类是一个组件
* Component:React提供的组件
*/
class Header extends Component {
/**
* render渲染函数,我们当前这个组件要显示模板,在render里面
* JSX代码
* render函数:方法重写,调用render优先执行子类render
* @returns
*/
render() {
return (
<React.Fragment>
<div className='container'>
<div className='logo'>
<img src={logo2} alt="" />
</div>
<div>
<span>欢迎【xx】登录</span>
</div>
</div>
<p></p>
</React.Fragment>
)
}
}
export default Header
总结:
- 服务器网络图片,地址直接写死,动态加载都可以。
- 图片本地图片,采用import 或者 require的方式引入进来,动态设置src属性
在JSX中动态绑定一个属性,我们需要src={logo}
(3)样式设计
在JSX中支持两种样式设计
- 内联样式:直接在标签上面style属性。
- 外部样式:将样式文件提取到外部css文件中,引入进来使用
内联样式:JSX中的样式已经经过封装过了。
<span style={{color:"red",fontSize:"20px"}}>欢迎【xx】登录</span>
因为style对象可以接受多个css样式,所以我们提供的是一个对象,定义很多样式
外部样式:
在styles文件夹下面创建css文件header.css
.container{
display: flex;
justify-content: space-between;
height: 80px;
width: 100%;
box-shadow: 0px 0px 10px rgba(0,0,0,.3);
padding: 0px 15px;
box-sizing: border-box;
}
.container .logo{
width: 180px;
height: 60px;
border: 1px solid red;
margin-top: 10px;
}
.container .logo img{
width: 100%;
}
JSX代码
render() {
return (
<React.Fragment>
<div className='container'>
<div className='logo'>
<img src={logo2} alt="" />
</div>
<div>
<span>欢迎【xx】登录</span>
</div>
</div>
<p></p>
</React.Fragment>
)
}
(4)样式模块化
在组件中样式导入过后默认是全局的样式,
不管在哪个组件中,一旦名字一致,相互影响
为了解决这个问题,React提出了一个概念。模块化样式。
开发步骤 styles文件夹下面创建文件名字 tabA.module.css
.op{
color: pink;
}
.box span{
color:blue
}
组件中使用
import styles from "../assets/tabA.module.css"
<div clasName={styles.box}></div>
(5) 组件的内部数据
Vue中组件内部数据使用data来定义。只要放在data中,这个数据响应式变化。一旦发生数据更新,页面也会更新
在React中组件内部数据采用state来定义
语法一:在构造器中定义state变量
constructor(){
//只要有继承,构造器第一句话必须写super
//super代表调用父类构造器,先创建父类
//当你不写构造器的时候,编辑器默认添加constructor
super()
//state对象里面存放的数据,类似于Vuedata定义的数据
this.state = {
msg:"小王"
}
}
页面上使用这个变量的时候
<div>{this.state.msg}</div>
为了更加优化代码,一般会在render函数里面解构,在去使用
render() {
const {msg} = this.state
return (
<React.Fragment>
<div className='menu'>
<ul>
<li className='active'>拼团管理</li>
<li>拼团数据</li>
<li>扩展内容</li>
</ul>
</div>
<div className='main'>
<p>{msg}</p>
<TabA></TabA>
<TabB></TabB>
</div>
</React.Fragment>
)
}
语法二:无需再构造器中定义
import React, { Component } from 'react'
import "../assets/styles/content.css"
import TabA from './TabA'
import TabB from './TabB'
import TabC from './TabC'
export default class Content extends Component {
state = {
msg:"xiaofeifei"
}
render() {
const {msg} = this.state
return (
<React.Fragment>
<div className='menu'>
<ul>
<li className='active'>拼团管理</li>
<li>拼团数据</li>
<li>扩展内容</li>
</ul>
</div>
<div className='main'>
<p>{msg}</p>
<TabA></TabA>
<TabB></TabB>
</div>
</React.Fragment>
)
}
}
(6)组件事件绑定
基本语法
React标签上面事件都被封装了一次。JSX的规则
命名规范采用驼峰命名方式
Vue
<div @click=“check”>
</div>
check(){
}
<div onClick={this.check}>
<div onChange={this.check}>
事件参数传递
<li onClick={this.check} className='active'>拼团管理</li>
绑定事件,提供事件函数,如果这个函数没有加括号。将函数地址放在这个模板中。
点击li元素,找到这个函数,并执行
<li onClick={this.check(123)} className='active'>拼团管理</li>
对于JSX来说,立即调用check这个函数。
解决这个问题方案:
<li onClick={()=>this.check("TabA")} className='active'>拼团管理</li>
给onClick这个事件绑定一个箭头函数,再这个箭头函数里面调用check函数。
只有事件被触发,箭头函数调用,check才会执行。传递参数过去
事件绑定中this指向问题
如果你函数是普通函数,默认拿到this结果为undefined
页面中绑定事件,节点来触发事件。React底层处理过,并不会直接返回节点。
import React, { Component } from 'react'
import styles from "../assets/styles/tabA.module.css"
export default class TabA extends Component {
state = {
count: 10
}
changeCount() {
//React底层处理过这个事件
console.log(this); //TabA
this.setState({
count:20
})
}
render() {
const { count } = this.state
return (
<div>
<p>state的数据:{count}</p>
<button onClick={this.changeCount.bind(this)}>点击触发</button>
</div>
)
}
}
如果函数是普通函数,绑定事件bind来改变this的指向,函数里面this指向当前组件。
import React, { Component } from 'react'
import styles from "../assets/styles/tabA.module.css"
export default class TabA extends Component {
state = {
count: 10
}
changeCount = ()=>{
console.log(this); //TabA
}
render() {
const { count } = this.state
return (
<div>
<p>state的数据:{count}</p>
<button onClick={this.changeCount}>点击触发</button>
</div>
)
}
}
总结:以后再React开发过程中,能用箭头函数就直接用箭头函数。
事件对象
只要有事件绑定,那一定会有event事件对象产生。
默认绑定不传递参数的时候,事件函数第一个参数就是event对象
changeCount = (event)=>{
console.log(event.target);
}
<button onClick = {this.changeCount}>
如果你的事件函数要传递参数
changeCount = (event,val)=>{
console.log(event,val);
}
<button onClick={(event)=>this.changeCount(event,123)}>点击触发</button>
遇到要传递参数的时候,外层箭头函数才是事件绑定的函数。传递给内部的函数使用
(7)组件数据更新
组件内部数据state来定义。直接修改state的值,页面并不会检测更新
check(val){
this.setState({
active:val
})
}
当React检测到调用这个函数,将原来值进行替换。render函数重新调用一次
当我们进行数据修改的时候,setState里面传递的新的对象,修改的值和会原来state的值进行合并。不会覆盖
更新规则
setState在更新数据的时候。合并操作。并不是覆盖操作
state = {
count: 10,
msg:"xiaowang"
}
changeCount = (event,val)=>{
this.setState({
count:val
})
}
修改的时候,指定了修改的变量。state原来的值没有变化,并不会被覆盖。
异步更新
setState这个函数,可以更新state的数据,并且调用render实现页面刷新。
但是本身是一个异步任务。无法立即获取修改的结果
changeCount = (event,val)=>{
//异步代码。
this.setState({
count:val
})
console.log(this.state.count); //原来的数据
}
为了性能考虑,设计的方案,不管是Vue还是React,我们都是异步更新
为了能获取更新过后的结果,我们提供回调函数
changeCount = (event, val) => {
//异步代码。
this.setState({
count: val
}, () => {
console.log(this.state.count); //得到更新过后结果
})
}
合并更新
setState一旦调用,数据发生更新,render就会被执行
changeCount = (event, val) => {
//异步代码。
this.setState({
count: val
})
this.setState({
count: 1000
})
}
一次性执行多个二setState,每个更新任务,都会放在队列中。
队列中任务会取出来进行合并,当发现多个任务的时候。后进入队列中将内容和前面任务合并了。
合并过后,只需要更新页面一次。不要频繁的触发
五、组件通信
父子组件通信
- 父传子:在父组件中定义值,通过动态参数传递给子组件,子组件接受结果
- 子传父:子组件调用父组件传递过来函数,将内容传递回去
父传子
组件的数据来源于两部分:
- 组件内部自身状态,state来定义
- 组件外部的状态,props接受
父组件
state = {
currentPage:1,
pageSize:3
}
const {currentPage,pageSize} = this.state
<Pageing currentPage={currentPage} pageSize={pageSize}>
子组件
如果是类组件,默认在this对象身上会有一个props的对象,这个对象默认代表外部数据。如果你传递。空对象
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"
export default class Pageing extends Component {
//mounted 挂载完毕
componentDidMount(){
console.log(this.props); -->{currentPage:1,pageSize:3}
}
render() {
return (
<div className={style.fenye}>
</div>
)
}
}
将props的值取出来,页面渲染。父组件中变量产生数据变化,子组件跟着变化
子组件如果要针对props的数据进行处理,一般是在子组件计算属性。
不要出现将props的值赋值给state,然后再去用state
验证
父组件调用子组件的时候,如果遇到子组件针对参数有验证规则。我们严格按照子组件要求来传递参数
子组件可以添加验证规则,一旦验证规则生效。使用这个组件,可以根据规则来传递参数
数据类型校验
在React老版本中,需要我们自己下载验证包,但是在新版本里面无需下载,直接使用
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"
import PropTypes from "prop-types"
class Pageing extends Component {
//mounted 挂载完毕
get totalPage() {
const { pageSize, total } = this.props
return Math.ceil(total / pageSize)
}
render() {
const { currentPage, pageSize, total } = this.props
return (
<div className={style.fenye}>
....
</div>
)
}
}
//在暴露给外部调用之前,我们内部先进行props的验证
Pageing.propTypes = {
currentPage:PropTypes.number.isRequired,
pageSize:PropTypes.number.isRequired
}
export default Pageing
指定接受的参数要进行验证。
也可以设置默认值
import React, { Component } from 'react'
import style from "../../assets/styles/fenye.module.css"
import PropTypes from "prop-types"
class Pageing extends Component {
//mounted 挂载完毕
get totalPage() {
const { pageSize, total } = this.props
return Math.ceil(total / pageSize)
}
render() {
const { currentPage, pageSize, total } = this.props
return (
<div className={style.fenye}>
....
</div>
)
}
}
Pageing.defaultProps = {
currentPage:3
}
export default Pageing
如果你没有传递对应属性,采用默认props值。父组件传递了属性。以父组件的数据为主
子传父
父组件传递一个函数给子组件,子组件调用这个函数。就可以将参数传递过去
父组件
getCurrentPage = (page)=>{
}
<Pageing getCurrentPage={this.getCurrentPage}>
子组件调用
fenye = (event)=>{
const page = event.target.getAttribute("index")
console.log(typeof page) //string
this.props.getCurrentPage(Number(page))
}
<span index={1} onClick = {this.fenye}>1</span>
六、组件封装插槽
在React中子组件需要接受父组件定义模板。可以用插槽来接受数据
import React, { Component } from 'react'
import style from "../../assets/styles/model.module.css"
export default class Model extends Component {
render() {
return (
<div className={style.container}>
<div className={style.modelContent}>
<h3>添加优惠券</h3>
{this.props.children}
{/* 存放表单 */}
<button>关闭</button>
</div>
</div>
)
}
}
this.props.children
代表接受父组件传递过来模板
七、表单组件
在React中没有提供Vue双向绑定语法,v-model
如果要操作表单,我们有两种模式。受控组件,非受控组件。
受控组件
含义:每个表单都会涉及到value属性或者checked属性,这些属性你需要获取到结果。
当我们定义state数据,和表单value或者checked属性建立绑定关系。将这个组件受控组件
受控组件开发步骤:
-
需要在state中定义属性
state = { pname:"新人券" }
-
在表单元素上面,使用value属性显示state里面的结果
<input value={this.state.pname}>
input元素就变成受控组件。
-
必须给受控组件,提供一个onChange事件
如果只是单向将state的数据,绑定文本框的value属性。可以实现数据显示。
无法修改数据
控制台默认抛出警告
ynameChange = (event)=>{ this.setState({ yname:event.target.value }) } <input onChange={this.ynameChange} value={this.state.pname}>
受控组件就已经实现了。
这个过程就是v-model实现过程。Vue将双向绑定过程封装v-model语法糖
完整的表单通过受控组件获取参数
import React, { Component } from 'react'
import styles from "../assets/styles/tabA.module.css"
import Pageing from './UIComponent/Pageing'
import Model from './UIComponent/Model'
export default class TabA extends Component {
state = {
tableData: [
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2023-01-13 17:39:42",
"2023-02-13 17:39:42"
],
_id: "63919f95024a7602dc78082a",
title: "测试优惠券",
receiveType: "2",
price: 30,
state: false,
show: false,
system_data: true,
create_time: "2022-12-08T08:25:57.209Z",
imgSrc: "https://pic.616pic.com/ys_bnew_img/00/06/12/6QLoLGyq3C.jpg"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2024-01-13 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b18",
title: "跨店满减",
receiveType: "1",
price: 200,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2024-01-13 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b19",
title: "新人券",
receiveType: "2",
price: 400,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2024-01-13 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b20",
title: "满100减20",
receiveType: "3",
price: 80,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2022-01-15 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b21",
title: "数码产品券",
receiveType: "1",
price: 60,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2022-02-13 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b22",
title: "跨店满减",
receiveType: "2",
price: 90,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
},
{
time: [
"2021-01-13 17:39:42",
"2023-01-13 17:39:42"
],
useTime: [
"2022-01-13 17:39:42",
"2022-01-13 17:39:42"
],
_id: "63bd14c40d58511b7c6b6b23",
title: "中秋现礼券",
receiveType: "3",
price: 10,
state: true,
show: true,
create_time: "2023-01-10T07:33:24.011Z",
imgSrc: "https://pic2.zhimg.com/v2-12943651b8b7fb48144151e638c8ed81_1440w.jpg?source=172ae18b"
}
],
searchType: "",
searchData: "",
status: 0,
orderBy: 0,
currentPage: 1,
pageSize: 3,
visible1: false,
visible2: false,
yname: "",
yprice: "",
ytype: "",
ytime: [],
ystate: ""
}
/**
* 优惠券添加
* @returns
*/
ynameChange = (event) => {
const value = event.target.value;
this.setState({
yname: value
})
}
ytypeChange = (event) => {
// "true"
// Boolean("false") false
const value = event.target.value;
this.setState({
ystate: JSON.parse(value)
})
}
ytimeChange = (event) => {
const value = event.target.value
const { ytime } = this.state
if (event.target.checked) {
ytime.push(value)
}else {
const index = ytime.findIndex(item=>item.ytime == value)
ytime.splice(index,1)
}
this.setState({
ytime
})
}
render() {
return (
<React.Fragment>
{/* 添加优惠券的模态框 */}
<Model title="添加优惠券" close={this.modelClose1} visible={visible1}>
{/* 插槽的概念 */}
<div className='item'>
<label htmlFor="">优惠券名字:</label>
<input type="text" onChange={this.ynameChange} value={this.state.yname} placeholder='请输入优惠券名字' />
</div>
<div className='item'>
<label htmlFor="">优惠券价格:</label>
<input type="text" onChange={(e) => this.setState({ yprice: e.target.value })} value={this.state.yprice} />
</div>
<div className='item'>
<label htmlFor="">优惠券类型:</label>
<select name="" id="" onChange={(e) => this.setState({ ytype: e.target.value })} value={this.state.ytype}>
<option value="">请选择</option>
<option value="1">新人券</option>
<option value="2">门槛券</option>
<option value="3">通用券</option>
</select>
</div>
<div className='item'>
<label htmlFor="">优惠券状态:</label>
<span>开启</span>
<input type="radio" onChange={this.ytypeChange} value={true} name='state' />
<span>关闭</span>
<input type="radio" onChange={this.ytypeChange} value={false} name='state' />
</div>
<div>
<label htmlFor="">日期:</label>
<span>2023-01-02</span>
<input type="checkbox" onChange={this.ytimeChange} value="2023-01-02" />
<span>2023-04-02</span>
<input type="checkbox" onChange={this.ytimeChange} value="2023-04-02" />
</div>
<div className='item'>
<button>添加</button>
</div>
</Model>
{/* 修改优惠券模态框 */}
<Model title="修改优惠券" close={this.modelClose2} visible={visible2}>
<input type="text" />
</Model>
</React.Fragment>
)
}
}
非受控组件
概念
回顾:表单元素value属性或者checked属性跟state的状态绑定在一起。表单元素内部的状态被state控制了。如果要进行修改提供change事件获取值,更新state达到state的值更新,页面表单值或者状态更新
非受控组件:表单的值和state没有直接绑定在一起。表单是独立了。值也可以动态修改。可以获取表单的值,保存到state中。
介绍两个运算符
?.
:
{/* ?.代表可选链运算符,这是ES2020新增的特性。当useTime undefined或者null 不会继续往后执行 */}
{this.state.product.useTime?.indexOf("2023-01-13 17:39:42")}
当检测到?.
前面结果是undefined或者null的时候,结束继续往后面寻找
this.state.product.useTime && this.state.product.useTime.indexOf()
??
:
let m = a ?? "xiaowang"
当检测到a这个变量是undefined或者null的时候。默认返回??后面结果
回显
在表单组件中,如果要使用非受控组件来进行数据回显。
可以使用defaultValue
和defaultChecked
<div className='item'>
<label htmlFor="">优惠券名字:</label>
<input disabled defaultValue={this.state.product.title} type="text" placeholder='请输入优惠券名字' />
</div>
<div className='item'>
<label htmlFor="">优惠券类型:</label>
<select name="" id="" defaultValue={this.state.product.receiveType}>
<option value="">请选择</option>
<option value="1">新人券</option>
<option value="2">门槛券</option>
<option value="3">通用券</option>
</select>
</div>
<div className='item'>
<label htmlFor="">优惠券状态:</label>
<span>开启</span>
<input type="radio" defaultChecked={this.state.product.state==true} value={true} name='state' />
<span>关闭</span>
<input type="radio" defaultChecked={this.state.product.state==false} value={false} name='state' />
</div>
<div>
<label htmlFor="">日期:</label>
<span>2023-01-02</span>
<input type="checkbox" defaultChecked={this.state.product.useTime?.includes("2023-01-13 17:39:42")} value="2023-01-13 17:39:42" />
<span>2023-04-02</span>
<input type="checkbox" defaultChecked={this.state.product.useTime?.includes("2023-02-13 17:39:42")} value="2023-02-13 17:39:42" />
</div>
获取表单的数据
非受控组件采用获取节点,在获取数据。更新到state中
针对表单
<input ref={callback}/>
callback回调函数,传递ref,调用函数并将节点返回给函数。保存起来
<input ref={element=>this.titleElement=element}/>
将得到节点默认挂载this全局。以后再任何一个函数中,都可以通过this来获取
再函数中获取到表单的值
update = ()=>{
const element = this.titleElement //获取input文本框
const value = element.value
}
完整绑定
<div className='item'>
<label htmlFor="">优惠券名字:</label>
<input ref={element => this.titleElement = element} defaultValue={this.state.product.title} type="text" placeholder='请输入优惠券名字' />
</div>
<div className='item'>
<label htmlFor="">优惠券类型:</label>
<select ref={element => this.typeElement = element} name="" id="" defaultValue={this.state.product.receiveType}>
<option value="">请选择</option>
<option value="1">新人券</option>
<option value="2">门槛券</option>
<option value="3">通用券</option>
</select>
</div>
<div className='item'>
<label htmlFor="">优惠券状态:</label>
<span>开启</span>
<input type="radio" ref={element => this.stateElement1 = element} defaultChecked={this.state.product.state == true} value={true} name='state' />
<span>关闭</span>
<input type="radio" ref={element => this.stateElement2 = element} defaultChecked={this.state.product.state == false} value={false} name='state' />
</div>
<div>
<label htmlFor="">日期:</label>
<span>2023-01-02</span>
<input type="checkbox" ref={element => this.timeElement1 = element} defaultChecked={this.state.product.useTime?.includes("2023-01-13 17:39:42")} value="2023-01-13 17:39:42" />
<span>2023-04-02</span>
<input type="checkbox" ref={element => this.timeElement2 = element} defaultChecked={this.state.product.useTime?.includes("2023-02-13 17:39:42")} value="2023-02-13 17:39:42" />
</div>
<div>
<button onClick={this.updateMsg}>修改</button>
</div>
对应的js代码如下
updateMsg = () => {
const title = this.titleElement.value;
const receiveType = this.typeElement.value;
if (this.stateElement1.checked) {
console.log(this.stateElement1.value);
} else {
console.log(this.stateElement2.value);
}
if (this.timeElement1.checked) {
console.log(this.timeElement1.value);
}
if (this.timeElement2.checked) {
console.log(this.timeElement2.value);
}
//将获取结果,用于覆盖product对象
const {product,tableData} = this.state
let newObj = {...product,title,receiveType}
//替换tableData中原来的这个对象
const index = tableData.findIndex(item=>item._id == newObj._id)
tableData[index] = newObj
this.setState({
tableData
})
}
事件委托获取表单值
采用事件委托的方式,我们可以动态构造一个对象,获取值存放对象中
每个表单元素都有change事件,事件委托在父级添加一个change
每个表单元素新增name属性,这个属性就是你想要的对象key
import React, { Component } from 'react'
export default class TabB extends Component {
state = {
obj: {
useTime: []
}
}
contenChange = (event) => {
const { obj } = this.state
const element = event.target
const value = element.value
//获取element这个表单组件的name属性。对象key,value对象值
const key = element.name
if (key == "useTime") {
if (element.checked) {
obj.useTime.push(value)
} else {
const index = obj.useTime.findIndex(item => item == value)
obj.useTime.splice(index, 1)
}
} else {
obj[key] = value
}
this.setState({
obj
})
}
render() {
return (
<div onChange={this.contenChange}>
<div className='item'>
<label htmlFor="">优惠券名字:</label>
<input type="text" name='title' placeholder='请输入优惠券名字' />
</div>
<div className='item'>
<label htmlFor="">优惠券价格:</label>
<input type="text" name='price' />
</div>
<div className='item'>
<label htmlFor="">优惠券类型:</label>
<select name="receiveType" id="">
<option value="">请选择</option>
<option value="1">新人券</option>
<option value="2">门槛券</option>
<option value="3">通用券</option>
</select>
</div>
<div className='item'>
<label htmlFor="">优惠券状态:</label>
<span>开启</span>
<input type="radio" value={true} name='state' />
<span>关闭</span>
<input type="radio" value={false} name='state' />
</div>
<div>
<label htmlFor="">日期:</label>
<span>2023-01-02</span>
<input type="checkbox" name='useTime' value="2023-01-02" />
<span>2023-04-02</span>
<input type="checkbox" name='useTime' value="2023-04-02" />
</div>
<div className='item'>
<button>添加</button>
</div>
</div>
)
}
}
八、组件生命周期
Vue应用生命周期
main.js
import Vue from "vue"
import App from ""
const app = new Vue({
router,
store,
created(){
},
mounted(){
}
}).$mount("#app")
单页面开发,整个应用程序,只需要new一次Vue
关闭项目,Vue才会销毁。
组件生命周期
因为涉及到单页面开发,引入路由来链接你们组件。路由切换组件创建或者销毁
组件生命周期:
- 挂载阶段:组件创建完成后,被加载到应用中。
- 更新阶段:组件的state(内部数据),组件props(外部数据)发生变化。完成更新阶段
- 销毁阶段:离开这个组件的时候,默认销毁组件
挂载阶段
进入组件中
- 执行构造器,创建当前组件实例
- 执行render函数,返回要挂载的模板。React加载模板并渲染
- 执行componentDidMount,实际上表示模板已经加载完毕
// mounted
componentDidMount(){
console.log("03-componentDidMount");
}
可以在这个生命周期函数中,发送异步请求。获取页面DOM节点
更新阶段
组件state或者props发生数据变化,都要进入第二步
更新之前
// 更新阶段,获取更新props,更新state
shouldComponentUpdate(newProps,newState){
const {count} = this.state
if(count == newState.count){
return false
}
return true
}
这个生命周期函数,需要提供boolean值,
true:允许页面更新,接下来调用render函数
false: 阻止更新
props发生变化,这个生命也执行
componentDidUpdate(){
console.log("componentDidUpdate");
}
先执行render,完成渲染。调用更新完毕的函数
销毁阶段
销毁的时候,可以在生命周期函数中,执行资源清理
componentWillUnmount(){
console.log("componentWillUnmount");
}
比如关闭定时器、解除元素的事件绑定、比如state数据清空等等
转载自:https://juejin.cn/post/7277390540852232247