效果图: css
src文件夹结构图:
react
import React, { Component } from 'react'
import Item from '../item/index'
import './index.css'
import PropTypes from 'prop-types'
export default class List extends Component {
// 对接受的props进行类型和必要的限制
static propTypes = {
updateTodo:PropTypes.func.isRequired,
deleteTodo:PropTypes.func.isRequired,
todos:PropTypes.array.isRequired,
}
render() {
const {todos,updateTodo,deleteTodo} = this.props
console.log('todos',todos)
return (
<ul className='TodoListUl'> { todos.map(item => { return( <Item deleteTodo={deleteTodo} updateTodo={updateTodo} {...item} key={item.id}/> ) }) } </ul>
)
}
}
// css
.TodoListUl{
margin: 0;
padding: 0;
width: 508px;
margin-top: 10px;
}
复制代码
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state={
mouse:false
}
// 勾选
handleCheck=(id)=>{
return (e)=>{
this.props.updateTodo(id,e.target.checked)
}
}
// 鼠标移入移除
handleMouse=(val)=>{
return ()=>{
this.setState({mouse:val})
}
}
// 删除todo
deleteTodo=(id)=>{
return ()=>{
const {deleteTodo} = this.props
if(window.confirm('肯定删除么?')){
deleteTodo(id)
}
}
}
render() {
const {id,name,done} = this.props
const {mouse} = this.state
return (
<li style={{backgroundColor:mouse?'#ddd':'white'}} onMouseLeave={this.handleMouse(false)} onMouseEnter={this.handleMouse(true)} className='todoListItem'> <span className='left'> <input checked={done} onChange={this.handleCheck(id)} type='checkbox'/> <span>{name}</span> </span> <button onClick={this.deleteTodo(id)} className='button' style={{display:mouse?'block':'none'}}>删除</button> </li>
)
}
}
//css
.todoListItem{
list-style: none;
display: flex;
justify-content: space-between;
height: 40px;
line-height: 40px;
border:1px solid #ddd;
}
.left{
display: inline-block;
}
.button{
margin: 5px 8px 0 0;
border: none;
height: 30px;
width: 60px;
border-radius: 4px;
background-color: red;
color: #fff;
}
复制代码
import React, { Component } from 'react'
import './index.css'
import PropTypes from 'prop-types'
export default class Header extends Component {
// 对接受的props进行类型和必要的限制
static propTypes = {
addTodo:PropTypes.func.isRequired
}
// 键盘事件的回调
handleKeyUp=(e)=>{
// 解构keyCode target
const {keyCode,target} = e
// 回车键
if(keyCode !== 13) return
if(!target.value){
alert('任务不能为空!')
return
}
console.log('Enter')
let todoObj = {
id:this.getNums(),
name:target.value,
done:false
}
this.props.addTodo(todoObj)
target.value = ''
}
getNums=()=>{
return Math.round(Math.random()*200)
}
render() {
return (
<input onKeyUp={this.handleKeyUp} className='todoheader' placeholder='请输入你的任务,按回车键确认'/>
)
}
}
//css
.todoheader{
width: 500px;
height: 30px;
}
复制代码
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
// 全选
handleCheckAll=(e)=>{
this.props.handleCheckAll(e.target.checked)
}
// 清楚全部已完成
handleClearAllDone=()=>{
this.props.handleClearAllDone()
}
render() {
const {todos} = this.props
// reduce 数组条件统计 条件求和 筛选最值(MDN查看文档)
// 已完成的个数
const doneCount = todos.reduce((pre,todo)=>{ return pre+(todo.done?1:0)},0)
// 所有个数
const total = todos.length
console.log('doneCount',doneCount)
return (
<div className='footerbox'> <input type='checkbox' onChange={this.handleCheckAll} checked={doneCount===total &&total!==0?true:false}/> <span className='checkboxSpan'>已完成{doneCount}/所有{total}</span> <button onClick={this.handleClearAllDone} className='deleteAllButton'>删除已完成任务</button> </div>
)
}
}
//css
.footerbox{
margin-top: 20px;
line-height: 40px;
}
.checkboxSpan{
margin-left:20px;
}
.deleteAllButton{
margin: 5px 8px 0 0;
border: none;
height: 30px;
width: 120px;
border-radius: 4px;
background-color: red;
color: #fff;
float: right;
}
复制代码
import React,{Component} from 'react'
import Header from './components/header'
import List from './components/list'
import Footer from './components/footer'
import './App.css'
export default class App extends Component{
// 状态在哪里,操做状态的方法就在哪里
// 初始化状态
state={
todos:[
{id:'001',name:'吃饭',done:true},
{id:'002',name:'睡觉',done:true},
{id:'003',name:'撸代码',done:false},
]
}
// 添加一个todo 获取的是todo对象
addTodo=(data)=>{
// 获取原todos数组
const {todos} = this.state
// 追加一个todo对象
const newTodos = [data,...todos]
this.setState({todos:newTodos})
}
// 修改完成未完成状态
updateTodo=(id,done)=>{
const { todos } = this.state
// 匹配处理数据
const newTodos = todos.map(item=>{
if(item.id===id) return {...item,done}
else return item
})
this.setState({todos:newTodos})
}
// 删除一条todo
deleteTodo=(id)=>{
const {todos} = this.state
const newTodos = todos.filter(item=>{
return item.id !== id
})
this.setState({todos:newTodos})
}
// 全选
handleCheckAll=(done)=>{
const {todos} = this.state
// 加工数据
const newTodos = todos.map(item=>{
return {...item,done}
})
this.setState({todos:newTodos})
}
// 清除全部已完成
handleClearAllDone=()=>{
const {todos} = this.state
// 加工数据
const newTodos = todos.filter(item=>{
return item.done === false
})
this.setState({todos:newTodos})
}
render(){
const { todos } = this.state
console.log('apptodos',todos)
return (
<div className='todoListBox'> <h2>豆豆是个小呆瓜</h2> <Header addTodo={this.addTodo}/> <List todos={todos} updateTodo={this.updateTodo} deleteTodo={this.deleteTodo}/> <Footer handleClearAllDone={this.handleClearAllDone} handleCheckAll={this.handleCheckAll} todos={todos}/> </div>
)
}
}
//css
.todoListBox{
width: 520px;
padding: 10px 0px 20px 10px;
border: 2px solid #ddd;
margin: auto;
border-radius: 6px;
}
复制代码
// 引入React核心库
import React from 'react'
// 引入ReactDOM
import ReactDOM from 'react-dom'
// 引入App组件
import App from './App'
// 渲染App到页面
ReactDOM.render(<App/>,document.getElementById('root'))
复制代码
1.拆分组件、实现静态组件,注意:className style的写法
2.动态初始化列表,如何肯定将数据放在哪一个组件的state中?
-某个组件使用:放在自身的state中 -某些组件使用:放在他们共同的父组件state中(状态提高) 3.关于父子之间通讯: 1.父组件给子组件传递数据:props传递 2.子组件给父组件传递数据:父组件经过props给子组件传递一个函数 4.注意defaultChecked和checked的区别 相似还有defaultValue和value 5.状态在哪里 操做状态的方法就在哪里 复制代码