持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
前言
做一个TodoList项目就是一个代办列表,网上发现好多这样的案例。代办事项的添加,删除,标记完成的功能。首先拆分组件,今天就写成一个完整的流程文档,从零开始吧
拆分组件&分析
分为Footer,Header,Item,List四个组件。现在没有做组件间的数据传递,把所有的数据操作都放在src/App.jsx
里面,注意现在我们在src
文件夹下编码的时候都采用jsx来写。
- 每一个事件都有一个ID,名字,是否完成的状态
- Header组件至少有一个输入框,回车自动插入数据,数据不能为空
- Item组件循环遍历数组,并且每一个元素有一个删除按钮
- Footer组件收集数组个数,完成状态的个数
App.js
import React, { Component } from 'react'
import Header from './components/Header/index'
import List from './components/List/index'
import Footer from './components/Footer/index'
import './App.css'
export default class App extends Component {
state = {todos:[]} //初始化状态
//增加列表的元素,传递一个并追加到数组中并重新渲染页面
addTodo = (todoObj)=>{
const {todos} = this.state
const newTodos = [todoObj,...todos]
this.setState({todos:newTodos})
}
//传ID去删除数组对象
deleteTodo = (id)=>{
//获取原来的todos
const {todos} = this.state
//删除指定id的todo对象
const newTodos = todos.filter((todoObj)=>{
return todoObj.id !== id
})
//更新状态
this.setState({todos:newTodos})
}
//清除所有已完成的
clearAllDone = ()=>{
const {todos} = this.state
const newTodos = todos.filter((todoObj)=>{
return !todoObj.done
})
this.setState({todos:newTodos})
}
render() {
const {todos} = this.state
return (
<div className="todo-container">
<div className="todo-wrap">
<Header addTodo={this.addTodo}/>
<List todos={todos} deleteTodo={this.deleteTodo}/>
<Footer todos={todos} checkAllTodo={this.checkAllTodo} clearAllDone={this.clearAllDone}/>
</div>
</div>
)
}
}
复制代码
- 初始化
state
为一个空数组 - JavaScript 方法中使用filter() 方法创建一个新数组,也叫数组过滤器, 其包含通过所提供函数实现的测试的所有元素,把测试通过的元素放在新数组里返回,不通过的丢弃。
- App里面的组件包含了整个项目的所有子组件。
Header
,List
,Footer
为同级组件。通过import引入,在import
关键字引入的时候先引入第三方组件再引入内置组件
有两层div标签包裹,其CSS文件App.css内容为:
/*base*/
body {
background: #fff;
}
.btn {
display: inline-block;
padding: 4px 12px;
margin-bottom: 0;
font-size: 14px;
line-height: 20px;
text-align: center;
vertical-align: middle;
cursor: pointer;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
border-radius: 4px;
}
.btn-danger {
color: #fff;
background-color: #da4f49;
border: 1px solid #bd362f;
}
.btn-danger:hover {
color: #fff;
background-color: #bd362f;
}
.btn:focus {
outline: none;
}
.todo-container {
width: 600px;
margin: 0 auto;
}
.todo-container .todo-wrap {
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
}
复制代码
复习一下之前之前没用过,或者印象不深的CSS,
-
vertical-align:设置元素的垂直对齐方式
-
cursor:规定了光标显示的形状 (悬浮在按钮上就变了)
-
box-shadow:实现图层阴影效果,有6个可设置属性,从左到右依次是:阴影类型 X轴位移 Y轴位移 阴影大小 阴影扩展 阴影颜色。
inset
为内阴影 -
:hover :选择获得焦点
-
:focus :选择鼠标悬停
Header
index.jsx:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import {nanoid} from 'nanoid'
import './index.css'
export default class Header extends Component {
static propTypes = {
addTodo:PropTypes.func.isRequired
}
//键盘单击触发事件
handleKeyUp = (event)=>{
//键盘单击事件event解构赋值
const {keyCode,target} = event
//回车按键KeyCode为13
if(keyCode !== 13) return
//添加的todo名字不能为空
if(target.value.trim() === ''){
alert('输入不能为空')
return
}
const todoObj = {id:nanoid(),name:target.value,done:false}
//将新增的数据集给传递给父组件
this.props.addTodo()
//清空输入框数据
target.value = ''
}
render() {
return (
<div className="todo-header">
<input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
</div>
)
}
}
复制代码
-
这里需要安装一个
nanoid
库,用来生成唯一不重复ID,按理来说这里应该交给后端处理,然后端插入到数据库返回自增ID。 -
子组件只能通过 props 来传递数据
index.css:
/*header*/
.todo-header input {
width: 560px;
height: 28px;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 4px;
padding: 4px 7px;
}
.todo-header input:focus {
outline: none;
border-color: rgba(82, 168, 236, 0.8);
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);
}
复制代码
又出现了 outline: none;
,今天测试一下效果: 使用了 outline: none;
点击输入框 没有使用了
outline: none;
点击输入框,box-shadow的蓝色阴影就无效了
Item
index.jsx:
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = {mouse_state:false}
//鼠标移入、移出的回调
handleMouse = (flag)=>{
return ()=>{
this.setState({mouse_state:flag})
}
}
//勾选、取消勾选某一个todo的回调
handleCheck = (id)=>{
return (event)=>{
this.props.updateTodo(id,event.target.checked)
}
}
//删除一个todo的回调
handleDelete = (id)=>{
if(window.confirm('确定删除吗?')){
this.props.deleteTodo(id)
}
}
render() {
const {id,name,done} = this.props
const {mouse_state} = this.state
return (
<li style={{backgroundColor:mouse_state ? '#ddd' : 'white'}} onMouseEnter={this.handleMouse(true)} onMouseLeave={this.handleMouse(false)}>
<label>
<input type="checkbox" checked={done} onChange={this.handleCheck(id)}/>
<span>{name}</span>
</label>
<button onClick={()=> this.handleDelete(id) } className="btn btn-danger" style={{display:mouse?'block':'none'}}>删除</button>
</li>
)
}
}
复制代码
这里有一个关键的效果,就是悬浮列表li标签的时候显示删除按钮。使用了两个事件:onMouseEnter
,onMouseLeave
,使用了同一个回调函数:handleMouse
,传了不同的函数做了不一样的效果展示。
index.css:
/*item*/
li {
list-style: none;
height: 36px;
line-height: 36px;
padding: 0 5px;
border-bottom: 1px solid #ddd;
}
li label {
float: left;
cursor: pointer;
}
li label li input {
vertical-align: middle;
margin-right: 6px;
position: relative;
top: -1px;
}
li button {
float: right;
display: none;
margin-top: 3px;
}
li:before {
content: initial;
}
li:last-child {
border-bottom: none;
}
复制代码
-
list-style:值为none示设置列表项标记的类型为空,即列表项前无标记,默认标记是实心圆,非常丑。
-
content:页面中的内容插入,initial:将此属性设置为其默认值
-
border-bottom: none: 表示最后一个li元素去掉下边框,会和ul下边框重复。
List
index.jsx:
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
//对接收的props进行:类型、必要性的限制
static propTypes = {
todos:PropTypes.array.isRequired,
updateTodo:PropTypes.func.isRequired,
deleteTodo:PropTypes.func.isRequired,
}
render() {
const {todos,updateTodo,deleteTodo} = this.props
return (
<ul className="todo-main">
{
todos.map( todo =>{
return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo}/>
})
}
</ul>
)
}
}
复制代码
主要是做一个列表外层容器,在组件使用了map方法,并且对其函数的参数进行限制。没有特别的功能 index.css:
/*main*/
.todo-main {
margin-left: 0px;
border: 1px solid #ddd;
border-radius: 2px;
padding: 0px;
}
.todo-empty {
height: 40px;
line-height: 40px;
border: 1px solid #ddd;
border-radius: 2px;
padding-left: 5px;
margin-top: 10px;
}
复制代码
效果&总结
代码写完了,看看效果:
主要是拆分组件的思想还有组件间的数据传递,数据操作,还了解到了一些CSS细节,还需要继续锻炼学习,感觉自己练习太少了,总结太少了,好多不用不实际操作都理解什么意思。