【React】初体验 路由 + antd完成案例TodoList

一、案例界面展示

请添加图片描述
请添加图片描述

二、组件拆分

在这里插入图片描述

三、安装依赖

在这里插入图片描述

四、文件架构

components目录下是普通组件,pages目录下是路由组件

在这里插入图片描述

根组件App.jsx

import React, {
    
     Component } from 'react'
import TodoList from './pages/TodoList'
import Home from './pages/Home'
import Count from './pages/Count'
import "./App.css"
import {
    
     Route, NavLink, Routes, Navigate } from 'react-router-dom'

export default class App extends Component {
    
    

  render() {
    
    
    return (
      <div className='container'>
        <div className='routes'>
          <NavLink to="/home">Home</NavLink>&nbsp;|&nbsp;
          <NavLink to="/todoList">TodoList</NavLink>&nbsp;|&nbsp;
          <NavLink to="/count">Count</NavLink>
        </div>
        <Routes>
          <Route path="/home" element={
    
    <Home/>}/>
          <Route path="/todoList" element={
    
    <TodoList/>}/>
          <Route path="/count" element={
    
    <Count/>}/>
          <Route path="*" element={
    
    <Navigate to="/home"/>}/>
        </Routes>
      </div>
    )
  }
}

普通组件Header

import React, {
    
     Component } from 'react'
import {
    
     Input } from 'antd';
import {
    
     nanoid } from 'nanoid';

export default class Header extends Component {
    
    
    // 绑定input框的值
    state = {
    
    
        value:""
    }
    // 回车事件的回调
    handleKeyUp = (e) => {
    
    
        // 解构赋值获取keyCode,target
        let {
    
     keyCode, target } = e
        // 屏蔽输入空值
        if(target.value.trim() === ""){
    
    
            alert("输入的内容不能为空")
            return false
        }
        // 判断是否回车键
        if(keyCode === 13){
    
    
            // 准备新增的tudo对象
            const todoObj = {
    
    id:nanoid(),name:target.value,isChecked:false}
            // 将todo对象传递给父组件App
            this.props.addTodo(todoObj)
            // 添加完清空输入值
            this.setState({
    
    
                value:""
            })
        }
    }
    // 绑定input的value
    changeVal = (e) => {
    
    
        let {
    
     target } = e
        this.setState({
    
    
            value:target.value
        })
    }
    render() {
    
    
        return (
            <div className='todo-header'>
                <Input placeholder="输入任务名称 回车确认" value={
    
    this.state.value} allowClear onPressEnter={
    
    this.handleKeyUp} onChange={
    
    this.changeVal}/>
            </div>
        )
    }
}

普通组件List

List ==> index.jsx

List作为Item的父组件,需要集合所有的Item组件并且展示

import React, {
    
     Component } from 'react'
import Item from '../Item'
import "./index.css"

export default class List extends Component {
    
    
    render() {
    
    
    	// 接收父组件的数据和方法
        const {
    
     todos, changeTodo, deleteTodo } = this.props
        return (
            <ul className='todo-item-list'>
                {
    
    
                    todos.map( todo =>{
    
    
                        return <Item key={
    
    todo.id} {
    
    ...todo} changeTodo={
    
    changeTodo} deleteTodo={
    
    deleteTodo} />
                    })
                }
            </ul>
        )
    }
}

List ==> index.css

.todo-item-list{
    
    
    margin-top: 10px;
    padding: 0;
}

普通组件Item

Item ==> index.jsx

import React, {
    
     Component } from 'react'
import {
    
     Checkbox, Button } from 'antd';
import "./index.css"

export default class Item extends Component {
    
    
    state = {
    
    
        status:false
    }
    // 鼠标移入移出控制显示状态
    handleMouse = (falg) => {
    
    
        return ()=>{
    
    
            this.setState({
    
    
                status: falg
            })
        }
    }
    // 多选框选中状态
    changeCheckbox = (id) => {
    
    
        return (e) => {
    
    
            // 调用父组件的changeTodo方法,将参数传递过去
            this.props.changeTodo(id,e.target.checked)
        }
    }
    // 删除一个todo
    handleDelete = (id) => {
    
    
        if(window.confirm("确定删除吗?")){
    
    
            this.props.deleteTodo(id)
        }
    }
    render() {
    
    
        const {
    
    id,name,isChecked} = this.props
        const {
    
    status} = this.state
        return (
            <li className='todo-item' onMouseEnter={
    
    this.handleMouse(true)} onMouseLeave={
    
    this.handleMouse(false)}>
                <Checkbox checked={
    
    isChecked} onChange={
    
    this.changeCheckbox(id)}>{
    
    name}</Checkbox>
                <Button type="primary" danger size='small' onClick={
    
     () => this.handleDelete(id) } style={
    
    {
    
    "display":status ? "block" : "none"}} className='btn'>删除</Button>
            </li>
        )
    }
}

Item ==> index.css

.todo-item{
    
    
    height: 36px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0 10px;
}
.todo-item:hover{
    
    
    background-color: #eee;
}
.todo-item:hover .btn{
    
    
    display: block;
}
li{
    
    
    list-style: none;
}

普通组件Footer

import React, {
    
     Component } from 'react'
import {
    
     Checkbox, Button } from "antd"

export default class Footer extends Component {
    
    
    // 全选的回调
    handleCheckAll = (e) => {
    
    
        this.props.checkAll(e.target.checked)
    }
    // 清除所有已完成的
    handleDeleteAll = () => {
    
    
        if(window.confirm("确定清除所有已完成的吗")){
    
    
            this.props.clearAll()
        }
    }
    render() {
    
    
        const {
    
    todos} = this.props
        // 已完成的个数统计
        const checkedCount = todos.reduce((pre,current)=>{
    
    
            return pre + (current.isChecked ? 1 : 0)
        },0)
        // 总个数
        const total = todos.length
        return (
            <div className='footer'>
                <Checkbox onChange={
    
    this.handleCheckAll} checked={
    
    checkedCount === total && total !== 0 ? true : false}>已完成{
    
    checkedCount} / 全部{
    
    total}</Checkbox>
                <Button size='small' type='primary' style={
    
    {
    
    "float":"right"}} onClick={
    
    this.handleDeleteAll}>清除全部任务</Button>
            </div>
        )
    }
}

路由组件TodoList

TodoList组件是父组件,需要导入Header、Item、Footer个子组件展示,负责组件传值
这里需要考虑到数据存储,数据如何存储如何传递

  1. 初始化数据 state 存储在父组件中,由父组件负责帮助传递数据(在不使用redux的情况下)
  2. 在TodoList.jsx组件中定义状态state,通过标签属性传递给子组件的props中

index.jsx

import React, {
    
     Component } from 'react'
import Header from '../../components/Header'
import Footer from '../../components/Footer'
import List from '../../components/List'
import "./index.css"

export default class TodoList extends Component {
    
    
      // 初始化状态
    state = {
    
    
        todos:[
            {
    
    id:1,name:"吃饭",isChecked:true},
            {
    
    id:2,name:"睡觉",isChecked:true},
            {
    
    id:3,name:"打豆豆",isChecked:false},
        ]
    }

    // 添加一个todo todoObj是接收参数
    addTodo = (todoObj) =>{
    
    
        const {
    
     todos } = this.state
        // 最前面添加一个todo
        const newTodos = [todoObj,...todos]
        // 更新状态
        this.setState({
    
    
        	todos:newTodos
        })
    }

    // 更新多选框的选中状态
    changeTodo = (id,status) => {
    
    
        const {
    
     todos } = this.state
        // 匹配处理数据
        const newTodos = todos.map((todoObj) => {
    
    
        if(todoObj.id === id) return {
    
    ...todoObj,isChecked:status}
        else return todoObj
        })
        // 更新状态
        this.setState({
    
    
        todos:newTodos
        })
    }

    // 删除todo
    deleteTodo = (id) => {
    
    
        const {
    
     todos } = this.state
        // 删除指定id的todo
        const newTodos = todos.filter((todoObj)=>{
    
    
        return todoObj.id !== id
        })
        // 更新状态
        this.setState({
    
    
        todos:newTodos
        })
    }

    // 全选
    checkAll = (status) => {
    
    
        const {
    
     todos } = this.state
        // 匹配处理数据
        const newTodos = todos.map((todoObj) => {
    
    
        return {
    
    ...todoObj,isChecked:status}
        })
        // 更新状态
        this.setState({
    
    
        todos:newTodos
        })
    }

    // 清除所有已完成
    clearAll = () => {
    
    
        const {
    
     todos } = this.state
        // 清除状态为true的数据
        const newTodos = todos.filter((todoObj) => {
    
    
        return !todoObj.isChecked
        })
        // 更新状态
        this.setState({
    
    
        todos:newTodos
        })
    }
    render() {
    
    
        return (
            <div className='todo-container'>
                <Header addTodo={
    
    this.addTodo}/>
                <List todos={
    
    this.state.todos} changeTodo={
    
    this.changeTodo} deleteTodo={
    
    this.deleteTodo}/>
                <Footer todos={
    
    this.state.todos} checkAll={
    
    this.checkAll} clearAll={
    
    this.clearAll}/>
            </div>
        )
    }
}

index.css

.todo-container{
    
    
    border: 1px solid #ccc;
    padding: 20px;
}

路由组件Home

import React, {
    
     Component } from 'react'

export default class Home extends Component {
    
    
    render() {
    
    
        return (
            <div className='home'>
                Home
            </div>
        )
    }
}

路由切换效果

请添加图片描述

总结

  1. 需要考虑到初始化数据state存放的位置,父子组件传值通过 props 传递
  2. 注意render函数中,class的写法应该为className,style应该为style={ {}}
  3. react-router-dom升级到v6和v5版本的写法有些差别,Switch换成Routes,v5路由写法<Route path="/home" components={Home}/>,v6路由写法<Route path="/home" element={<Home/>}/>。路由重定向v5写法<Redirect to="/home/" />,路由重定向v6写法<Route path="*" element={<Navigate to="/home"/>}/>

猜你喜欢

转载自blog.csdn.net/m0_48995032/article/details/122601157