文章目录
一、案例界面展示
二、组件拆分
三、安装依赖
四、文件架构
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> |
<NavLink to="/todoList">TodoList</NavLink> |
<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个子组件展示,负责组件传值
这里需要考虑到数据存储,数据如何存储如何传递
- 初始化数据
state
存储在父组件中,由父组件负责帮助传递数据(在不使用redux的情况下)- 在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>
)
}
}
路由切换效果
总结
- 需要考虑到初始化数据state存放的位置,父子组件传值通过
props
传递 - 注意render函数中,class的写法应该为className,style应该为style={{}}
- 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"/>}/>