效果圖: 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.狀態在哪裏 操做狀態的方法就在哪裏 複製代碼