【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"/>}/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值