初始化脚手架
使用 npx 创建
1 npx create-react-app 项目名
使用 npm install 创建
全局安装create-react-app包:
1 npm install -g create-react-app
创建脚手架:
npm 镜像
执行 create-react-app
时,还会自动安装一些包,这个时候,默认使用的是npm
,速度较慢,可以替换为国内源。
1 2 3 4 npm config set registry https://registry.npm.taobao.org npm config get registry
若已经安装cpm
,则全局安装 create-react-app
时可以使用 cnpm
,但需要注意的是,在执行 create-react-app my-app
时,还会自动安装一些包,这个时候,默认使用的还是npm
。
脚手架文件结构
public
src
App.js
定义名为App的组件
App.css
App组件的样式表
index.js
入口文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import React from 'react' ;import ReactDOM from 'react-dom' ;import './index.css' ;import App from './App' ;import reportWebVitals from './reportWebVitals' ;ReactDOM.render( <React.StrictMode > <App /> </React.StrictMode > , document .getElementById('root' ) ); reportWebVitals();
App组件外包裹React.StrictMode
标签可以对App组件代码进行检查,如:警告字符串类型的ref
index.css
全局通用样式表,也可放在public文件夹中,在index.html中用link标签引入(不建议,破坏结构)
reportWebVitals.js
记录页面性能
setupTests.js
做组件测试(整体测试或单元测试)
一个简单的Hello案例
index.js
1 2 3 4 5 6 7 8 9 import React from 'react' import ReactDOM from 'react-dom' import App from './App' ReactDOM.render(<App /> , document .getElementById('root' ))
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import React, {Component} from "react" import Hello from "./Hello" import Welcome from "./components/Welcome" export default class App extends Component { render ( ) { return ( <div > <Hello /> <Welcome /> </div > ) } }
Hello.js
1 2 3 4 5 6 7 8 9 import React, {Component} from "react" export default class Hello extends Component { render ( ) { return ( <h1 > Hello React</h1 > ) } }
Welcome.jsx
1 2 3 4 5 6 7 8 9 10 import React, {Component} from "react" import './Welcome.css' export default class Welcome extends Component { render ( ) { return ( <h1 className ="wel" > Welcome to line2!</h1 > ) } }
Welcome.css
1 2 3 .wel { color : blueviolet; }
效果
需要注意的点
js文件可以更改为jsx文件(index.js也是)
import React, {Component} from "react"
里的{Component}
能够引用是因为React.Component
被局部暴露,即:未暴露不能这么写,可以写成:const {Component} = React
,继承时不再写extends React.Component
,直接写extends Component
即可
组件文件夹中为每个子组件创建文件夹,可以有两种形式:
文件位置:components/Welcome/Welcome.js
,引用方式:import Welcome from "./components/Welcome/Welcome"
文件位置:components/Welcome/index.js
,引用方式:import Welcome from "./components/Welcome"
样式的模块化:将Welcome.css
更名为Welcome.module.css
,随后在对应组件中引入import welcome from './index.module.css'
组件完整代码如下:
1 2 3 4 5 6 7 8 9 10 import React, {Component} from "react" import welcome from './index.module.css' export default class Welcome extends Component { render ( ) { return ( <h1 className ={welcome.wel} > Welcome to line2!</h1 > ) } }
功能界面的组件化编码流程
拆分组件:拆分界面,抽取组件
实现静态组件:使用组件实现静态页面效果
实现动态组件
动态显示初始化数据(状态)
交互(从绑定事件监听开始)
TodoList案例
文件结构
静态页面准备
index.js
1 2 3 4 5 import React from 'react' import ReactDOM from 'react-dom' import App from './App' ReactDOM.render(<App /> , document .getElementById('root' ))
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import React, { Component } from 'react' import Header from './components/Header/Header' import List from './components/List/List' import Footer from './components/Footer/Footer' import './App.css' export default class App extends Component { render ( ) { return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header /> <List /> <Footer /> </div > </div > ) } }
App.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 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 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 import React, { Component } from 'react' import './Header.css' export default class Header extends Component { render ( ) { return ( <div className ="todo-header" > <input type ="text" placeholder ="请输入你的任务名称,按回车键确认" /> </div > ) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 .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 ); }
List.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import React, { Component } from 'react' import Item from '../Item/Item' import './List.css' export default class List extends Component { render ( ) { return ( <ul className ="todo-main" > <Item /> <Item /> </ul > ) } }
List.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 .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 ; }
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 import React, { Component } from 'react' import './Item.css' export default class Item extends Component { render ( ) { return ( <li > <label > <input type ="checkbox" /> <span > xxxxx</span > </label > <button className ="btn btn-danger" style ={{display: 'none '}}> 删除</button > </li > ) } }
Item.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 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; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import React, { Component } from 'react' import './Footer.css' export default class Footer extends Component { render ( ) { return ( <div className ="todo-footer" > <label > <input type ="checkbox" /> </label > <span > <span > 已完成0</span > / 全部2 </span > <button className ="btn btn-danger" > 清除已完成任务</button > </div > ) } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 .todo-footer { height : 40px ; line-height : 40px ; padding-left : 6px ; margin-top : 5px ; } .todo-footer label { display : inline-block; margin-right : 20px ; cursor : pointer; } .todo-footer label input { position : relative; top : -1px ; vertical-align : middle; margin-right : 5px ; } .todo-footer button { float : right; margin-top : 5px ; }
动态初始化列表
目前没有学消息订阅与发布,兄弟组件间无法通信,所以将状态放在App组件中。
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 state = {todoList :[ {id : 1 , name : '吃饭' , done : true }, {id : 2 , name : '睡觉' , done : true }, {id : 3 , name : '写代码' , done : false } ]} render ( ) {const {todoList} = this .statereturn ( <div className ="todo-container" > <div className ="todo-wrap" > <Header /> <List todoList ={todoList}/ > <Footer /> </div > </div > )
List组件接收后使用展开运算符传递给Item组件
List.jsx
1 2 3 4 5 6 7 8 9 10 11 12 render ( ) { const {todoList} = this .props return ( <ul className ="todo-main" > { todoList.map((todo) => { return <Item key ={todo.id} {...todo }/> }) } </ul > ) }
Item组件接收props并展示
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 render ( ) { const {id, name, done} = this .props return ( <li > <label > <input type ="checkbox" defaultChecked ={done}/ > // 不能使用checked,会将选定状态写死,defaultChecked可能产生bug <span > {name}</span > </label > <button className ="btn btn-danger" style ={{display: 'none '}}> 删除</button > </li > ) }
效果如下:
实现添加一个Todo
Header
组件中为input
添加onKeyUp
事件
Header.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 export default class Header extends Component { handleKeyUp = (event ) => { const {target, key} = event if (key !== 'Enter' ) { return } console .log(target.value) } render ( ) { return ( <div className ="todo-header" > <input onKeyUp ={this.handleKeyUp} type ="text" placeholder ="请输入你的任务名称,按回车键确认" /> </div > ) } }
App
组件向Header
组件传递addTodo
函数
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 addTodo = (todoObj ) => { const {todoList} = this .state const newTodos = [todoObj, ...todoList] this .setState({todoList : newTodos}) } render ( ) {const {todoList} = this .statereturn ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList}/ > <Footer /> </div > </div > )
Header
组件onKeyUp
对输入数据进行处理并传回App
组件
Header.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 handleKeyUp = (event ) => { const {target, key} = event if (key !== 'Enter' ) { return } if (target.value.trim() === '' ) { alert('输入不能为空' ) return } const todoObj = {id : nanoid(), name : target.value, done : false } this .props.addTodo(todoObj) target.value = '' }
nanoid
使用nanoid或uuid生成唯一id值
执行npm i nanoid
或yarn add nanoid
安装依赖,使用时执行引入语句import { nanoid } from 'nanoid'
,调用nanoid()
函数即可
效果
鼠标悬浮效果
为Item
组件设置state
,并添加鼠标移入移出监听
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 export default class Item extends Component { state = {mouse : false } handleMouse = flag => { return () => { this .setState({mouse : flag}) } } render ( ) { const {id, name, done} = this .props const {mouse} = this .state return ( <li style ={{backgroundColor: mouse ? '#ddd ' : '#fff '}} 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 > ) } }
复选框更新状态
App
组件通过List
组件向Item
组件传递updateTodo
函数
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 updateTodo = (id, done ) => { const {todoList} = this .state const newTodos = todoList.map((todoObj ) => { if (todoObj.id === id) { return {...todoObj, done} } else { return todoObj } }) this .setState({todoList : newTodos}) } render ( ) { const {todoList} = this .state return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList} updateTodo ={this.updateTodo}/ > <Footer /> </div > </div > )
List.jsx
1 return <Item key ={todo.id} {...todo } updateTodo ={updateTodo}/ >
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 handleChange = 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 ' : '#fff '}} onMouseEnter ={this.handleMouse(true)} onMouseLeave ={this.handleMouse(false)} > <label > <input type ="checkbox" defaultChecked ={done} onChange ={this.handleChange(id)}/ > <span > {name}</span > </label > <button className ="btn btn-danger" style ={{display: mouse ? 'block ' : 'none '}}> 删除</button > </li > ) }
对props进行限制
引入prop-types库
1 import PropTypes from 'prop-types'
Header.jsx
1 2 3 4 static propTypes = { addTodo : PropTypes.func.isRequired }
List.jsx
1 2 3 4 5 static propTypes = { todoList : PropTypes.array.isRequired, updateTodo : PropTypes.func.isRequired }
实现删除一个Todo
与addTodo
和updateTodo
实现同理,App通过List向Item传递deleteTodo
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 deleteTodo = (id ) => { const {todoList} = this .state const newTodos = todoList.filter((todoObj ) => { return todoObj.id !== id }) this .setState({todoList : newTodos}) } render ( ) { const {todoList} = this .state return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList} updateTodo ={this.updateTodo} deleteTodo ={this.deleteTodo}/ > <Footer /> </div > </div > ) }
List.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 export default class List extends Component { static propTypes = { todoList : PropTypes.array.isRequired, updateTodo : PropTypes.func.isRequired, deleteTodo : PropTypes.func.isRequired } render ( ) { const {todoList, updateTodo, deleteTodo} = this .props return ( <ul className ="todo-main" > { todoList.map((todo) => { return <Item key ={todo.id} {...todo } updateTodo ={updateTodo} deleteTodo ={deleteTodo}/ > }) } </ul > ) } }
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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 ' : '#fff '}} onMouseEnter ={this.handleMouse(true)} onMouseLeave ={this.handleMouse(false)} > <label > <input type ="checkbox" defaultChecked ={done} onChange ={this.handleChange(id)}/ > <span > {name}</span > </label > <button onClick ={() => this.handleDelete(id) } className="btn btn-danger" style={{display: mouse ? 'block' : 'none'}}>删除</button > </li > ) }
JavaScript中,关于消息提示框的方法有三个:
alert(message)方法用于显示带有一条指定消息和一个 OK 按钮的警告框。
confirm(message)方法用于显示一个带有指定消息和 OK 及取消按钮的对话框。如果用户点击确定按钮,则 confirm() 返回 true。如果点击取消按钮,则 confirm() 返回 false。
prompt(text,defaultText)方法用于显示可提示用户进行输入的对话框。如果用户单击提示框的取消按钮,则返回 null。如果用户单击确认按钮,则返回输入字段当前显示的文本。
重要
将defaultChecked
修改为checked
defaultChecked
指定复选框的初始状态,一旦指定后无法修改
已完成/全部 + 全选
已完成
App
组件向Footer
组件传递todoList
Footer
组件计算已完成数目和总数
全选
App
组件向Footer
组件传递checkAllTodo
Footer
组件完成全选checkbox
的回调
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 checkAllTodo = (done ) => { const {todoList} = this .state const newTodos = todoList.map((todoObj ) => { return {...todoObj, done} }) this .setState({todoList : newTodos}) } render ( ) { const {todoList} = this .state return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList} updateTodo ={this.updateTodo} deleteTodo ={this.deleteTodo}/ > <Footer todoList ={todoList} checkAllTodo ={this.checkAllTodo}/ > </div > </div > ) }
Footer.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 export default class Footer extends Component { handleChangeAll = (event ) => { this .props.checkAllTodo(event.target.checked) } render ( ) { const {todoList} = this .props const doneCount = todoList.reduce((pre, currentTodo ) => pre + (currentTodo.done ? 1 : 0 ), 0 ) const total = todoList.length return ( <div className ="todo-footer" > <label > <input type ="checkbox" onChange ={this.handleChangeAll} checked ={doneCount === total && total !== 0}/ > </label > <span > <span > 已完成{doneCount}</span > / 全部{total} </span > <button className ="btn btn-danger" > 清除已完成任务</button > </div > ) } }
清除全部已完成
App
组件向Footer
组件传递clearAllDoneTodo
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 clearAllDoneTodo = () => { const {todoList} = this .state const newTodos = todoList.filter((todoObj ) => { return !todoObj.done }) this .setState({todoList : newTodos}) } render ( ) { const {todoList} = this .state return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList} updateTodo ={this.updateTodo} deleteTodo ={this.deleteTodo}/ > <Footer todoList ={todoList} checkAllTodo ={this.checkAllTodo} clearAllDoneTodo ={this.clearAllDoneTodo}/ > </div > </div > ) }
Footer.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 this .handleClearAll = () => { this .props.clearAllDoneTodo() } return ( <div className ="todo-footer" > <label > <input type ="checkbox" onChange ={this.handleChangeAll} checked ={doneCount === total && total !== 0}/ > </label > <span > <span > 已完成{doneCount}</span > / 全部{total} </span > <button onClick ={this.handleClearAll} className ="btn btn-danger" > 清除已完成任务</button > </div > )
完整逻辑代码与最终效果
App.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 import React, { Component } from 'react' import Header from './components/Header/Header' import List from './components/List/List' import Footer from './components/Footer/Footer' import './App.css' export default class App extends Component { state = {todoList :[ {id : '1' , name : '吃饭' , done : true }, {id : '2' , name : '睡觉' , done : true }, {id : '3' , name : '写代码' , done : false } ]} addTodo = (todoObj ) => { const {todoList} = this .state const newTodos = [todoObj, ...todoList] this .setState({todoList : newTodos}) } updateTodo = (id, done ) => { const {todoList} = this .state const newTodos = todoList.map((todoObj ) => { if (todoObj.id === id) { return {...todoObj, done} } else { return todoObj } }) this .setState({todoList : newTodos}) } deleteTodo = (id ) => { const {todoList} = this .state const newTodos = todoList.filter((todoObj ) => { return todoObj.id !== id }) this .setState({todoList : newTodos}) } checkAllTodo = (done ) => { const {todoList} = this .state const newTodos = todoList.map((todoObj ) => { return {...todoObj, done} }) this .setState({todoList : newTodos}) } clearAllDoneTodo = () => { const {todoList} = this .state const newTodos = todoList.filter((todoObj ) => { return !todoObj.done }) this .setState({todoList : newTodos}) } render ( ) { const {todoList} = this .state return ( <div className ="todo-container" > <div className ="todo-wrap" > <Header addTodo ={this.addTodo}/ > <List todoList ={todoList} updateTodo ={this.updateTodo} deleteTodo ={this.deleteTodo}/ > <Footer todoList ={todoList} checkAllTodo ={this.checkAllTodo} clearAllDoneTodo ={this.clearAllDoneTodo}/ > </div > </div > ) } }
Header.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import React, { Component } from 'react' import PropTypes from 'prop-types' import { nanoid } from 'nanoid' import './Header.css' export default class Header extends Component { static propTypes = { addTodo : PropTypes.func.isRequired } handleKeyUp = (event ) => { const {target, key} = event if (key !== 'Enter' ) { 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 > ) } }
List.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import React, { Component } from 'react' import PropTypes from 'prop-types' import Item from '../Item/Item' import './List.css' export default class List extends Component { static propTypes = { todoList : PropTypes.array.isRequired, updateTodo : PropTypes.func.isRequired, deleteTodo : PropTypes.func.isRequired } render ( ) { const {todoList, updateTodo, deleteTodo} = this .props return ( <ul className ="todo-main" > { todoList.map((todo) => { return <Item key ={todo.id} {...todo } updateTodo ={updateTodo} deleteTodo ={deleteTodo}/ > }) } </ul > ) } }
Item.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 import React, { Component } from 'react' import './Item.css' export default class Item extends Component { state = {mouse : false } handleMouse = flag => { return () => { this .setState({mouse : flag}) } } handleChange = 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 ' : '#fff '}} onMouseEnter ={this.handleMouse(true)} onMouseLeave ={this.handleMouse(false)} > <label > <input type ="checkbox" checked ={done} onChange ={this.handleChange(id)}/ > <span > {name}</span > </label > <button onClick ={() => this.handleDelete(id) } className="btn btn-danger" style={{display: mouse ? 'block' : 'none'}}>删除</button > </li > ) } }
Footer.jsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import React, { Component } from 'react' import './Footer.css' export default class Footer extends Component { handleChangeAll = (event ) => { this .props.checkAllTodo(event.target.checked) } handleClearAll = () => { } render ( ) { const {todoList} = this .props const doneCount = todoList.reduce((pre, currentTodo ) => pre + (currentTodo.done ? 1 : 0 ), 0 ) const total = todoList.length this .handleClearAll = () => { this .props.clearAllDoneTodo() } return ( <div className ="todo-footer" > <label > <input type ="checkbox" onChange ={this.handleChangeAll} checked ={doneCount === total && total !== 0}/ > </label > <span > <span > 已完成{doneCount}</span > / 全部{total} </span > <button onClick ={this.handleClearAll} className ="btn btn-danger" > 清除已完成任务</button > </div > ) } }
总结
拆分组件、实现静态组件,注意:className
、style
的写法
动态初始化列表,如何确定将数据放在哪个组件的state
中:
某个组件使用:放在自身的state中
某些组件使用:放在他们共同的父组件state中(状态提升)
关于父子之间通信:
【父组件】给【子组件】传递数据:通过props
传递
【子组件】给【父组件】传递数据:通过props
传递,要求父提前给子传递一个函数
注意defaultChecked
和checked
的区别,类似的还有:defaultValue和value
状态在哪里,操作状态的方法就在哪里