bashnpx create-react-app 项目名
全局安装create-react-app包:
bashnpm install -g create-react-app
创建脚手架:
bashcreate-react-app 项目名
执行 create-react-app
时,还会自动安装一些包,这个时候,默认使用的是npm
,速度较慢,可以替换为国内源。
bash# 换源
npm config set registry https://registry.npmmirror.com/
# 查看修改的结果
npm config get registry
若已经安装
cnpm
,则全局安装create-react-app
时可以使用cnpm
,但需要注意的是,在执行create-react-app my-app
时,还会自动安装一些包,这个时候,默认使用的还是npm
。
favicon.ico 偏爱图标,设置默认图标
index.html 详见注释(英文注释为react自动生成)
html<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<!-- %PUBLIC_URL% 代表public文件夹的路径 -->
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<!-- 开启理想视口,用于移动端适配 -->
<meta name="viewport" content="width=device-width, initial-scale=1" />
<!-- 用于配置浏览器页签和地址栏颜色(仅支持Android手机浏览器) -->
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<!-- 用于指定网页添加到手机桌面后的图标(iPhone) -->
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
应用加壳时的配置文件
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<!-- 若浏览器不支持js则展示标签中的内容 -->
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
robots.txt 爬虫协议规则文件,限制网站爬虫抓取
App.js 定义名为App的组件
App.css App组件的样式表
index.js 入口文件
jsximport 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')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
App组件外包裹React.StrictMode
标签可以对App组件代码进行检查,如:警告字符串类型的ref
index.css 全局通用样式表,也可放在public文件夹中,在index.html中用link标签引入(不建议,破坏结构)
reportWebVitals.js 记录页面性能
setupTests.js 做组件测试(整体测试或单元测试)
index.js
jsx// 引入React核心库
import React from 'react'
// 引入React-DOM
import ReactDOM from 'react-dom'
// 引入App组件
import App from './App' // .js后缀可省略
// 渲染App到页面
ReactDOM.render(<App/>, document.getElementById('root'))
App.js
jsx// 创建“外壳”组件App
import React, {Component} from "react" // 这里的{Component}能够引用是因为React.Component被局部暴露
import Hello from "./Hello"
import Welcome from "./components/Welcome"
// 创建并暴露App组件
export default class App extends Component {
render() {
return(
<div>
<Hello/>
<Welcome/>
</div>
)
}
}
// 暴露App组件
// export default App
Hello.js
jsximport React, {Component} from "react"
export default class Hello extends Component {
render() {
return(
<h1>Hello React</h1>
)
}
}
Welcome.jsx
jsximport React, {Component} from "react"
import './Welcome.css'
export default class Welcome extends Component {
render() {
return(
<h1 className="wel">Welcome to line2!</h1>
)
}
}
Welcome.css
css.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'
组件完整代码如下:
jsximport 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>
)
}
}
jsximport React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
ReactDOM.render(<App/>, document.getElementById('root'))
jsximport 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>
)
}
}
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;
}
jsximport React, { Component } from 'react'
import './Header.css'
export default class Header extends Component {
render() {
return (
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认"/>
</div>
)
}
}
css.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);
}
jsximport 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>
)
}
}
css.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;
}
jsximport 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>
)
}
}
css 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;
}
jsximport 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>
)
}
}
css.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
jsx// 初始化状态
state = {todoList:[
{id: 1, name: '吃饭', done: true},
{id: 2, name: '睡觉', done: true},
{id: 3, name: '写代码', done: false}
]}
render() {
const {todoList} = this.state
return (
<div className="todo-container">
<div className="todo-wrap">
<Header/>
<List todoList={todoList}/>
<Footer/>
</div>
</div>
)
List组件接收后使用展开运算符传递给Item组件
List.jsx
jsxrender() {
const {todoList} = this.props
return (
<ul className="todo-main">
{
todoList.map((todo) => {
return <Item key={todo.id} {...todo}/>
})
}
</ul>
)
}
Item组件接收props并展示
Item.jsx
jsxrender() {
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>
)
}
效果如下:
Header
组件中为input
添加onKeyUp
事件
Header.jsx
jsxexport default class Header extends Component {
handleKeyUp = (event) => {
const {target, key} = event // 不建议使用keyCode
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
jsx// addTodo用于添加一个todo,接收的参数是todo对象
addTodo = (todoObj) => {
// 获取原todoList
const {todoList} = this.state
// 追加一个todo
const newTodos = [todoObj, ...todoList]
// 更新状态
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}/>
<Footer/>
</div>
</div>
)
Header
组件onKeyUp
对输入数据进行处理并传回App
组件
Header.jsx
jsxhandleKeyUp = (event) => {
// 解构赋值获取target, key
const {target, key} = event
// 判断是否是回车
if (key !== 'Enter') {
return
}
// 添加的todo名字不能为空
if (target.value.trim() === '') {
alert('输入不能为空')
return
}
// 准备一个todo对象
const todoObj = {id: nanoid(), name: target.value, done: false}
// 将todoObj传递给App
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
jsxexport 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
jsx// updateTodo用于更新一个todo对象
updateTodo = (id, done) => {
// 获取状态中的todoList
const {todoList} = this.state
// 加工数据(匹配处理)
const newTodos = todoList.map((todoObj) => {
if (todoObj.id === id) {
return {...todoObj, done} // 简写形式,{...todoObj, done: 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
jsxreturn <Item key={todo.id} {...todo} updateTodo={updateTodo}/>
Item.jsx
jsxhandleChange = 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>
)
}
引入prop-types库
jsximport PropTypes from 'prop-types'
Header.jsx
jsx// 对接收的props进行类型、必要性的限制
static propTypes = {
addTodo: PropTypes.func.isRequired
}
List.jsx
jsx// 对接收的props进行类型、必要性的限制
static propTypes = {
todoList: PropTypes.array.isRequired,
updateTodo: PropTypes.func.isRequired
}
与addTodo
和updateTodo
实现同理,App通过List向Item传递deleteTodo
App.jsx
jsx// deleteTodo用于删除一个todo对象
deleteTodo = (id) => {
// 获取原todoList
const {todoList} = this.state
// 删除指定id的todo对象
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
jsxexport default class List extends Component {
// 对接收的props进行类型、必要性的限制
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
jsx// 删除一个todo的回调
handleDelete = (id) => {
if (window.confirm('确定删除吗?')) { // 此处要加window,否则会报错Unexpected use of '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
jsx// checkAllTodo用于todo对象的全选
checkAllTodo = (done) => {
// 获取原todoList
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
jsxexport default class Footer extends Component {
// 全选checkbox的回调
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) // pre上一次的返回值
// console.log('@', doneCount)
// 总数
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
jsx// clearAllDoneTodo用于清除所有已完成任务
clearAllDoneTodo = () => {
// 获取原todoList
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
jsxthis.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
jsximport 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用于添加一个todo,接收的参数是todo对象
addTodo = (todoObj) => {
// 获取原todoList
const {todoList} = this.state
// 追加一个todo
const newTodos = [todoObj, ...todoList]
// 更新状态
this.setState({todoList: newTodos})
}
// updateTodo用于更新一个todo对象
updateTodo = (id, done) => {
// 获取状态中的todoList
const {todoList} = this.state
// 加工数据(匹配处理)
const newTodos = todoList.map((todoObj) => {
if (todoObj.id === id) {
return {...todoObj, done} // 简写形式,{...todoObj, done: done}
} else {
return todoObj
}
})
this.setState({todoList: newTodos})
}
// deleteTodo用于删除一个todo对象
deleteTodo = (id) => {
// 获取原todoList
const {todoList} = this.state
// 删除指定id的todo对象
const newTodos = todoList.filter((todoObj) => {
return todoObj.id !== id
})
// 更新状态
this.setState({todoList: newTodos})
}
// checkAllTodo用于todo对象的全选
checkAllTodo = (done) => {
// 获取原todoList
const {todoList} = this.state
// 加工数据
const newTodos = todoList.map((todoObj) => { return {...todoObj, done} })
// 更新状态
this.setState({todoList: newTodos})
}
// clearAllDoneTodo用于清除所有已完成任务
clearAllDoneTodo = () => {
// 获取原todoList
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
jsximport React, { Component } from 'react'
import PropTypes from 'prop-types'
import { nanoid } from 'nanoid'
import './Header.css'
export default class Header extends Component {
// 对接收的props进行类型、必要性的限制
static propTypes = {
addTodo: PropTypes.func.isRequired
}
handleKeyUp = (event) => {
// 解构赋值获取target, key
const {target, key} = event
// 判断是否是回车
if (key !== 'Enter') {
return
}
// 添加的todo名字不能为空
if (target.value.trim() === '') {
alert('输入不能为空')
return
}
// 准备一个todo对象
const todoObj = {id: nanoid(), name: target.value, done: false}
// 将todoObj传递给App
this.props.addTodo(todoObj)
// 清空输入
target.value = ''
}
render() {
return (
<div className="todo-header">
<input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/>
</div>
)
}
}
List.jsx
jsximport React, { Component } from 'react'
import PropTypes from 'prop-types'
import Item from '../Item/Item'
import './List.css'
export default class List extends Component {
// 对接收的props进行类型、必要性的限制
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
jsximport React, { Component } from 'react'
import './Item.css'
export default class Item extends Component {
state = {mouse: false}
// 鼠标移入移出的回调
handleMouse = flag => {
return () => {
this.setState({mouse: flag})
}
}
// 勾选、取消某一个todo的回调
handleChange = id => {
return (event) => {
this.props.updateTodo(id, event.target.checked)
}
}
// 删除一个todo的回调
handleDelete = (id) => {
if (window.confirm('确定删除吗?')) { // 此处要加window,否则会报错Unexpected use of '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
jsximport React, { Component } from 'react'
import './Footer.css'
export default class Footer extends Component {
// 全选checkbox的回调
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) // pre上一次的返回值
// console.log('@', doneCount)
// 总数
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
中:
props
传递props
传递,要求父提前给子传递一个函数defaultChecked
和checked
的区别,类似的还有:defaultValue和value
本文作者:Morales
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 License 许可协议。转载请注明出处!