第三章:React 应用(基于 React 脚手架)
二、组件的组合使用 - TodoList
3. 添加 todo
3.1 App
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 } ,
{ id : '004' , name : '逛街' , done : false }
] }
addTodo = ( todoObj ) => {
const { todos} = this . state
const newTodos = [ todoObj, ... todos]
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} / >
< Footer / >
< / div>
< / div>
)
}
}
3.2 Header
import React, { Component } from 'react'
import { nanoid} from 'nanoid'
import './index.css'
export default class Header extends Component {
handleKeyUp = ( event ) => {
const { keyCode, target} = event
if ( event. keyCode !== 13 ) return
if ( target. value. trim ( ) === '' ) {
alert ( '输入不能为空' )
return
}
const todoObj = { id : nanoid ( ) , name : target. value, done : false }
this . props. addTodo ( todoObj)
target. value = ''
}
render ( ) {
return (
< div className= "todo-header" >
< input onKeyUp= { this . handleKeyUp} type= "text" placeholder= "请输入你的任务名称,按回车键确认" / >
< / div>
)
}
}
4. 鼠标移入效果
Item
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = { mouse : false }
handleMouse = ( flag ) => {
return ( ) => {
this . setState ( { mouse : flag} )
}
}
render ( ) {
const { name, done} = this . props
const { mouse} = this . state
return (
< li style= { { backgroundColor : mouse ? '#ddd' : 'white' } } onMouseEnter= { this . handleMouse ( true ) } onMouseLeave= { this . handleMouse ( false ) } >
< label>
< input type= "checkbox" defaultChecked= { done} / >
< span> { name} < / span>
< / label>
< button className= "btn btn-danger" style= { { display : mouse? 'block' : 'none' } } > 删除< / button>
< / li>
)
}
}
5. 添加一个 todo
5.1 App
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 } ,
{ id : '004' , name : '逛街' , done : false }
] }
addTodo = ( todoObj ) => {
const { todos} = this . state
const newTodos = [ todoObj, ... todos]
this . setState ( { todos : newTodos} )
}
updateTodo = ( id, done ) => {
const { todos} = this . state
const newTodos = todos. map ( ( todoObj ) => {
if ( todoObj. id === id) return { ... todoObj, done}
else return todoObj
} )
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} updateTodo= { this . updateTodo} / >
< Footer / >
< / div>
< / div>
)
}
}
5.2 List
import React, { Component } from 'react'
import Item from '../Item'
import './index.css'
export default class List extends Component {
render ( ) {
const { todos, updateTodo} = this . props
return (
< ul className= "todo-main" >
{
todos. map ( todo => {
return < Item key= { todo. id} { ... todo} updateTodo= { updateTodo} / >
} )
}
< / ul>
)
}
}
5.3 Item
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = { mouse : false }
handleMouse = ( flag ) => {
return ( ) => {
this . setState ( { mouse : flag} )
}
}
handleCheck = ( id ) => {
return ( event ) => {
this . props. updateTodo ( id, event. target. checked)
}
}
render ( ) {
const { id, name, done} = this . props
const { mouse} = this . state
return (
< li style= { { backgroundColor : mouse ? '#ddd' : 'white' } } onMouseEnter= { this . handleMouse ( true ) } onMouseLeave= { this . handleMouse ( false ) } >
< label>
< input type= "checkbox" defaultChecked= { done} onChange= { this . handleCheck ( id) } / >
< span> { name} < / span>
< / label>
< button className= "btn btn-danger" style= { { display : mouse? 'block' : 'none' } } > 删除< / button>
< / li>
)
}
}
6. 对 props 进行限制
6.1 Header
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 ) => {
const { keyCode, target} = event
if ( event. keyCode !== 13 ) return
if ( target. value. trim ( ) === '' ) {
alert ( '输入不能为空' )
return
}
const todoObj = { id : nanoid ( ) , name : target. value, done : false }
this . props. addTodo ( todoObj)
target. value = ''
}
render ( ) {
return (
< div className= "todo-header" >
< input onKeyUp= { this . handleKeyUp} type= "text" placeholder= "请输入你的任务名称,按回车键确认" / >
< / div>
)
}
}
6.2 List
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
static propTypes = {
todos : PropTypes. array. isRequired,
updateTodo : PropTypes. func. isRequired
}
render ( ) {
const { todos, updateTodo} = this . props
return (
< ul className= "todo-main" >
{
todos. map ( todo => {
return < Item key= { todo. id} { ... todo} updateTodo= { updateTodo} / >
} )
}
< / ul>
)
}
}
7. 删除一个 todo
7.1 App
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 } ,
{ id : '004' , name : '逛街' , done : false }
] }
addTodo = ( todoObj ) => {
const { todos} = this . state
const newTodos = [ todoObj, ... todos]
this . setState ( { todos : newTodos} )
}
updateTodo = ( id, done ) => {
const { todos} = this . state
const newTodos = todos. map ( ( todoObj ) => {
if ( todoObj. id === id) return { ... todoObj, done}
else return todoObj
} )
this . setState ( { todos : newTodos} )
}
deleteTodo = ( id ) => {
const { todos} = this . state
const newTodos = todos. filter ( ( todoObj ) => {
return todoObj. id !== id
} )
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} updateTodo= { this . updateTodo} deleteTodo= { this . deleteTodo} / >
< Footer / >
< / div>
< / div>
)
}
}
7.2 List
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
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>
)
}
}
7.3 Item
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = { mouse : false }
handleMouse = ( flag ) => {
return ( ) => {
this . setState ( { mouse : flag} )
}
}
handleCheck = ( id ) => {
return ( event ) => {
this . props. updateTodo ( id, event. target. checked)
}
}
handleDelete = ( id ) => {
if ( window. confirm ( '确定删除吗?' ) ) {
this . props. deleteTodo ( id)
}
}
render ( ) {
const { id, name, done} = this . props
const { mouse} = this . state
return (
< li style= { { backgroundColor : mouse ? '#ddd' : 'white' } } onMouseEnter= { this . handleMouse ( true ) } onMouseLeave= { this . handleMouse ( false ) } >
< label>
< input type= "checkbox" defaultChecked= { 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>
)
}
}
8. 实现底部功能
8.1 App
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 } ,
{ id : '004' , name : '逛街' , done : false }
] }
addTodo = ( todoObj ) => {
const { todos} = this . state
const newTodos = [ todoObj, ... todos]
this . setState ( { todos : newTodos} )
}
updateTodo = ( id, done ) => {
const { todos} = this . state
const newTodos = todos. map ( ( todoObj ) => {
if ( todoObj. id === id) return { ... todoObj, done}
else return todoObj
} )
this . setState ( { todos : newTodos} )
}
deleteTodo = ( id ) => {
const { todos} = this . state
const newTodos = todos. filter ( ( todoObj ) => {
return todoObj. id !== id
} )
this . setState ( { todos : newTodos} )
}
checkAllTodo = ( done ) => {
const { todos} = this . state
const newTodos = todos. map ( ( todoObj ) => {
return { ... todoObj, done}
} )
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} updateTodo= { this . updateTodo} deleteTodo= { this . deleteTodo} / >
< Footer todos= { todos} checkAllTodo= { this . checkAllTodo} clearAllDone= { this . clearAllDone} / >
< / div>
< / div>
)
}
}
8.2 Header
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 ) => {
const { keyCode, target} = event
if ( keyCode !== 13 ) return
if ( target. value. trim ( ) === '' ) {
alert ( '输入不能为空' )
return
}
const todoObj = { id : nanoid ( ) , name : target. value, done : false }
this . props. addTodo ( todoObj)
target. value = ''
}
render ( ) {
return (
< div className= "todo-header" >
< input onKeyUp= { this . handleKeyUp} type= "text" placeholder= "请输入你的任务名称,按回车键确认" / >
< / div>
)
}
}
8.3 List
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item'
import './index.css'
export default class List extends Component {
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>
)
}
}
8.4 Item
import React, { Component } from 'react'
import './index.css'
export default class Item extends Component {
state = { mouse : false }
handleMouse = ( flag ) => {
return ( ) => {
this . setState ( { mouse : flag} )
}
}
handleCheck = ( id ) => {
return ( event ) => {
this . props. updateTodo ( id, event. target. checked)
}
}
handleDelete = ( id ) => {
if ( window. confirm ( '确定删除吗?' ) ) {
this . props. deleteTodo ( id)
}
}
render ( ) {
const { id, name, done} = this . props
const { mouse} = this . state
return (
< li style= { { backgroundColor : mouse ? '#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>
)
}
}
8.5 Footer
import React, { Component } from 'react'
import './index.css'
export default class Footer extends Component {
handleCheckAll = ( event ) => {
this . props. checkAllTodo ( event. target. checked)
}
handleClearAllDone = ( ) => {
this . props. clearAllDone ( )
}
render ( ) {
const { todos} = this . props
const doneCount = todos. reduce ( ( pre, todo ) => pre + ( todo. done ? 1 : 0 ) , 0 )
const total = todos. length
return (
< div className= "todo-footer" >
< label>
< input type= "checkbox" onChange= { this . handleCheckAll} checked= { doneCount === total && total !== 0 ? true : false } / >
< / label>
< span>
< span> 已完成{ doneCount} < / span> / 全部{ total}
< / span>
< button onClick= { this . handleClearAllDone} className= "btn btn-danger" > 清除已完成任务< / button>
< / div>
)
}
}
三、总结
1 .拆分组件、实现静态组件,注意:className、style的写法
2 .动态初始化列表,如何确定将数据放在哪个组件的state中?
——某个组件使用:放在其自身的state中
——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
3 .关于父子之间通信:
1 .【父组件】给【子组件】传递数据:通过props传递
2 .【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
4 .注意defaultChecked 和 checked的区别,类似的还有:defaultValue 和 value
5 .状态在哪里,操作状态的方法就在哪里