react表单组件
在react中 实时获取 表单组件 input , select, checkbox, radio 的值, 不能像vue使用v-model, 需要通过onChange监听数据的改变,通过setState
1.受控组件
2.非受控组件
1.受控组件
表单的value值交给react来管理,数据放在state中定义,通过setState修改表单的数据
1.通过onChange来监听表单事件
- 通过setState修改表单的数据
- 初始值使用value赋值,配合onChange使用
import React, { Component } from 'react'
export default class InputDemo extends Component {
state = {
username:'alice'
}
updateName = (ev)=>{
let curretnVal = ev.target.value;
// 使用setState管理数据
this.setState({
username:curretnVal
})
}
render() {
return (
<div>
<input type='text' value={this.state.username} onChange={this.updateName} />
</div>
)
}
}
2.非受控组件
表单的value值交给dom来管理, 不需要通过setState来管理数据
使用createRef 创建ref变量
方式一:在构造器constructor中定义
constructor(){
super();
// 创建ref变量
this.ageRef = React.createRef(); // 可以通过这个ref变量获取到模板中的dom
}
方式二:通过简写的方式
ageRef = React.createRef();
注意:
在类组件中使用简写的方式定义变量, 不需要加this, 但是在模板中使用的时候, 需要添加this
ageRef = React.createRef();
sendForm = ()=>{
// 想获取input dom节点的value值
console.log(this.ageRef.current); // 当前模板中的某个dom
let currentVal = this.ageRef.current.value;
console.log(currentVal);
}
<input type="text" defaultValue={'30'} ref={this.ageRef} />
<button onClick={this.sendForm}>提交</button>
非受控组件 如果需要给初始值, 使用defaultValue代替value属性
3.提交form表单数据
官方推荐: 使用受控组件管理form表单数据, 针对某一个很简单的form表单,只有一个input可以使用非受控处理;
如果form列表数据比较多, 还是使用 受控组件管理数据,示例如下:
import React, { Component } from 'react'
export default class FormTest extends Component {
state = {
username:'',
password:''
}
getFormValue = (ev)=>{
let target = ev.target;
let curValue = target.value;
let keyname = target.name; // 获取form表单中某一个input的name
this.setState({
[keyname]:curValue
},()=>{
// 可以获取到最新修改的数据
// console.log(this.state);
})
}
sendForm=()=>{
console.log(this.state,'获取form表单的数据');
}
render() {
return (
<form >
<ul>
<li>
{this.state.username}
<span>姓名:</span> <input type="text" name="username" onChange={this.getFormValue} />
</li>
<li>
<span>密码:</span> <input type="text" name="password" onChange={this.getFormValue} />
</li>
<li>
<span>手机号:</span> <input type="text" name="mobile" onChange={this.getFormValue} />
</li>
<li>
<span>性别:</span>
<label htmlFor="g1"> <input type="radio" name="gender" id='g1' value='1' onChange={this.getFormValue} /> 男</label>
<label htmlFor="g2"> <input type="radio" name="gender" id='g2' value='2' onChange={this.getFormValue} />女</label>
</li>
<li>
<span>城市</span>
<select name='city' onChange={this.getFormValue}>
<option value='1'>北京</option>
<option value='2'>上海</option>
</select>
</li>
</ul>
<button type='button' onClick={this.sendForm}>提交</button>
</form>
)
}
}
作业讲解todoList
import React, { Component } from 'react'
export default class TodoList extends Component {
// 创建ref变量, 用来获取todo的名字
inputRef = React.createRef();
// 组件内部数据
state = {
todoList:[{
name:'画画',
isFinished:false,
id:1
}]
}
// 添加数据
addTodo = ()=>{
let inputDom = this.inputRef.current;
// this.state.todoList.push({}) // 如果直接修改state中的数据,模板不会重新渲染
// 源数据 和 新数据的合并, 然后在通过setState写入
let newData = {
name:inputDom.value,
isFinished:false,
id:+new Date()
}
this.setState({
todoList:[...this.state.todoList,newData ] // 数组的合并
},()=>{
console.log('这里可以获取state中最新的数据');
// 添加成功后, input的value清空
this.inputRef.current.value = ''
// es5的方式获取某个dom
// let testDom = document.getElementById('testDom')
// testDom.value = ''
})
}
// 删除数据
deleteToDo = (id)=>{
let index = this.state.todoList.findIndex((option)=>{
return option.id == id
})
this.state.todoList.splice(index,1);
this.setState({
todoList:this.state.todoList
})
}
render() {
const {todoList,isFinished} = this.state;
return (
<div>
<h2>TodoList</h2>
<input type="text" ref={this.inputRef} id='testDom'/> <button onClick={this.addTodo}>添加</button> <br />
<ul>
{todoList.map((item)=>{
return <li key={item.id}>{item.name} -- {isFinished?'完成':'未完成'} <button onClick={()=>this.deleteToDo(item.id)}>删除</button> </li>
})}
</ul>
</div>
)
}
}
父子组建传值
父传子
通过props传值
父组件
// 组件内部数据
state = {
todoList:[{
name:'画画',
isFinished:false,
id:1
}]
}
<TaskList todolist={this.state.todoList}></TaskList>
子组件接收props
this.props.todolist
通过简写的方式, 直接接收父组件的props,然后在模板中渲染
子传父
思路: 在父组件调用子组件的时候, 传一个类型为函数的属性给子组件,然后在子组件调用这个函数类型的属性方法, 通过参数传递,就相当于调用父组件定义好的那个方法
父组件
// 删除task, 用来接收子组件给父组件的数据的
deleteTask=(index)=>{
console.log(index);
this.state.todoList.splice(index,1);
this.setState({
todoList:this.state.todoList
})
}
<TaskList delete = {this.deleteTask}></TaskList>
子组件
deleteToDo = (id)=>{
let index = this.props.todolist.findIndex((option)=>{
return option.id == id
})
// 子组件传父组件
console.log(this.props.delete);
this.props.delete(index) ; // 相当于在调用父组件的deleteTask这个方法
}
loading加载动画,静态部分
import React, { Component } from 'react'
import './main.scss'
export default class Loading extends Component {
render() {
return (
<div className='loading'>
<div className='box'>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div className='box'>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
)
}
}
样式main.scss编写
.loading {
width: 60px;
height: 60px;
border: 1px solid #ccc;
position: relative;
margin: 100px;
.box {
width: 100%;
height: 100%;
position: absolute;
span {
width: 10px;
height: 10px;
background: blue;
border-radius: 50%;
animation: run 1.5s linear infinite;
&:nth-child(1) {
position: absolute;
left: 0;
top: 0;
}
&:nth-child(2) {
position: absolute;
right: 0;
top: 0;
}
&:nth-child(3) {
position: absolute;
right: 0;
bottom: 0;
}
&:nth-child(4) {
position: absolute;
left: 0;
bottom: 0;
}
}
// 第一个盒子的点
&:nth-child(1) {
//第1个点 2
&:nth-child(1) { animation-delay: 0s; }
// //第3个点 3
&:nth-child(2) { animation-delay: -0.4s; }
//第5个点
&:nth-child(3) { animation-delay: -0.8s; }
//第7个点
&:nth-child(4) { animation-delay: -1.2s; }
}
// 第二个盒子的点
&:nth-child(2) {
transform: rotate(45deg);
span {
//第2个点 2
&:nth-child(1) { animation-delay: -0.2s; }
// //第4个点 3
&:nth-child(2) { animation-delay: -0.6s; }
//第6个点
&:nth-child(3) { animation-delay: -1.0s; }
//第8个点
&:nth-child(4) { animation-delay: -1.4s; }
}
}
}
}
// 1. 什么动画
// 2. 动画持续时间
// 3. 是不时一直在动
@keyframes run {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
如何在css中定义全局变量和局部变量
全局变量
:root {
// 全局变量
--color:red;
}
使用变量
.test {
color:var(--color)
}
局部变量
就是在某一个文件里定义变量
.loading {
--bgcolor:red;
}
使用变量和全局变量是一样的
background: var(--colorbg);
props属性
用来接收父组件给子组件传的属性, 支持各种数据类型 string, number,boolean, 函数等
<Loading color={'red'} num={1} isOpen={true}></Loading>
color:string字符串
num:number类型
isOpen: boolean
属性的默认值
// props属性的默认值
static defaultProps = {
color:'green',
size:'small'
}
属性的校验
react老版本中需要安装 prop-types插件, 新版的react v18版本已经内置了prop-types, 所以在新版本中无需安装,直接使用
先引入
import propType from 'prop-types'
在组件中使用
// propTypes 固定的写法 , 属性类型检测
static propTypes = {
color:propType.string,
size:propType.string
}
loading组件的封装
import React, { Component } from 'react'
import propType from 'prop-types'
import './main.scss'
var obj = {
min:0.5,
small:1,
big:2
}
export default class Loading extends Component {
// props属性的默认值 color='orange' size='min'
static defaultProps = {
color:'green',
size:'small'
}
// propTypes 固定的写法 , 属性类型检测
static propTypes = {
color:propType.string,
size:propType.string
}
// props类型检测
// color:颜色
// this.props.size - > small -> obj[this.props.size]
styleObj = {
'--colorbg':this.props.color ,
transform:'scale('+obj[this.props.size]+')'
}
render() {
return (
<div className='loading' style={this.styleObj}>
<div className='box'>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
<div className='box'>
<span></span>
<span></span>
<span></span>
<span></span>
</div>
</div>
)
}
}
React Router路由
和vue一样, 路由分2种方式, hash ,history模板
hash : 路由上会有个#, react内部是通过监听hashChange来改变路由
history: 没有# ,react内部是通过监听popState来改变路由的
课程中讲解的是react router的版本 v6
react-router-dom 浏览器web端路由, 已经包含了react-router
react-router-native 混合app react-native的路由
所以我们在开发的web端的时候,因为 包含了react-router, 所以只需要安装react-router-dom
路由的安装
yarn add react-router-dom // npm install react-router-dom
路由的配置
history模式: BrowserRouter
hash 模式: HashRouter
我们这里以history模式为例
import {BrowserRouter,Routes,Route} from 'react-router-dom'
import Home from './pages/Home';
import About from './pages/About';
....
<BrowserRouter>
<Routes>
<Route path='/home' element={<Home></Home>}></Route>
<Route path='/about' element={<About></About>} ></Route>
</Routes>
</BrowserRouter>
...
总结:
- 路由配置必须在根组件app.js文件中配置
- BrowserRouter或者HashRouter 必须包裹住 路由配置Routes, react-routetr - (v6版本)Routes是用来管理Route
路由的重定向
通过Navigate组件 , 重定向到某一个路由组件
import {BrowserRouter,Routes,Route,Navigate} from 'react-router-dom'
<Routes>
<Route path='/' element={<Navigate to={'/home'}></Navigate>}></Route>
....
</Routes>
404路由
如果路由不在路由配置表中, 会加载404组件的模板
<Routes>
.....
<Route path='*' element={<div>404页面</div>}></Route>
</Routes>
路由的嵌套
route可以嵌套配置, 配置嵌套路由的时候, Route的api属性和第一级路由完全一致
<Routes>
<Route path='/' element={<Navigate to={'/home'}></Navigate>}></Route>
<Route path='/home' element={<Home></Home>}>
{/* 方式一: 不加/ */}
<Route path='user' element={<User></User>}></Route>
{/* 方式二: 添加/, 必须是父路由path+子路由path */}
<Route path='/home/role' element={<Role></Role>} ></Route>
</Route>
<Route path='*' element={<div>404页面</div>}></Route>
</Routes>
父路由配置:
import React, { Component } from 'react'
import {Outlet} from 'react-router-dom'
export default class Home extends Component {
render() {
return (
<div>
Home
<div className='main-home'>
{/* 二级路由的模板 */}
{/* Outlet 类似vue里的 view-router, 用来渲染子路由模板文件 */}
<Outlet></Outlet>
</div>
</div>
)
}
}
注意:
1.配置子路由的时候, 需要在父路由模板上添加 Outlet, 类似vue里的 view-router, 用来渲染子路由模板文件
2.配置path路径的时候, 可以加/,也可以不加
不加/ , 访问二级路由的时候, 路径是 父路由path+ 子路由path /home/user
加/, 访问二级路由的时候,路径就是当前配置的这个路由
NavLink导航组件
import {NavLink} from 'react-router-dom'
<div className='menu-top'>
<NavLink to={'/home'}>首页</NavLink>
<NavLink to={'/about'}>关于</NavLink>
</div>