React类组件 React基础 Day02

49 阅读6分钟

react表单组件

在react中 实时获取 表单组件 input , select, checkbox, radio 的值, 不能像vue使用v-model, 需要通过onChange监听数据的改变,通过setState

1.受控组件

2.非受控组件

1.受控组件

表单的value值交给react来管理,数据放在state中定义,通过setState修改表单的数据

1.通过onChange来监听表单事件

  1. 通过setState修改表单的数据
  2. 初始值使用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>
    )
  }
}

bg

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>
...

总结:

  1. 路由配置必须在根组件app.js文件中配置
  2. 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>