TodoList案例

一 TodoList案例_静态组件

需要理解的概念有:

  • 根据目标页面进行组件拆分规划
  • 样式内容的变化className与style的变化
  • 样式文件的拆分App.css以及各个模块的xxx.css样式文件
  • 如果是全局样式,可以考虑在public里进行引入

todos的静态页面(opens new window)

src/App.js

import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';function App() {return (<div className='todo-container'><div className='todo-wrap'><Header /><List /><Footer /></div></div>);
}export default App;

src/App.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;
}

src/components/Header/index.js

import React, { Component } from 'react';
import './index.css';
export default class Header extends Component {render() {return (<div className='todo-header'><input type='text' placeholder='请输入你的任务名称,按回车键确认' /></div>);}
}

src/components/Header/index.css

/*header*/
.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);
}

src/components/Footer/index.js

import React, { Component } from 'react';
import './index.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>);}
}

src/components/Footer/index.css

/*footer*/
.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;
}

src/components/List/index.js

import React, { Component } from 'react';
import Item from '../Item';
import './index.css';export default class List extends Component {render() {return (<ul className='todo-main'><Item /><Item /></ul>);}
}

src/components/List/index.css

/*main*/
.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;
}

src/components/Item/index.js

import React, { Component } from 'react';
import './index.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>);}
}

src/components/Item/index.css

/*item*/
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;
}

二 TodoList案例_初始化列表

需要理解的概念有:

  • 状态的初始化设置
  • 属性的传递与接收
  • 属性的逐一传递与批量属性传递
  • checkbox的checked设置与defaultChecked设置

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};render() {// 解构赋值const { todos } = this.state;return (<div className='todo-container'><div className='todo-wrap'><Header /><List todos={todos} /><Footer /></div></div>);}
}export default App;

src/components/List/index.js

import React, { Component } from 'react';
import Item from '../Item';
import './index.css';export default class List extends Component {render() {const { todos } = this.props;return (<ul className='todo-main'>{todos.map((todo) => {
// return <Item key={todo.id} id={todo.id} name={todo.name} done={todo.done} />;
// 利用展开式参数传递进行批量属性传递return <Item key={todo.id} {...todo} />;})}</ul>);}
}

src/components/Item/index.js

import React, { Component } from 'react';
import './index.css';
export default class Item extends Component {render() {const { id, name, done } = this.props;return (<li><label>{/*checkbox设置checked会报警告信息根据建议换成defaultChecked属性*/}<input type='checkbox' defaultChecked={done} /><span>{name}</span></label><button className='btn btn-danger' style={{ display: 'none' }}>删除</button></li>);}
}

三 TodoList 案例_添加一个 todo

需要理解的概念有:

  • 将方法以属性方式传递,在子组件调用,父组件回调
  • 键盘事件,判断触发的按键
  • 唯一码的生成,安装与引入第三方模块
  • 数组对象的合并与新增
  • 清空输入框内容

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};render() {// 解构赋值const { todos } = this.state;return (<div className='todo-container'><div className='todo-wrap'><Header addTodo={this.addTodo} /><List todos={todos}/><Footer /></div></div>);}// addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) => {const { todos } = this.state;// 追加一个todoconst newTodos = [todoObj, ...todos];// 更新状态this.setState({todos: newTodos,});};
}export default App;

src/components/Header/index.js

import React, { Component } from 'react';
import './index.css';
import { nanoid } from 'nanoid';export default class Header extends Component {render() {return (<div className='todo-header'><input type='text' onKeyUp={this.handleKeyUp} placeholder='请输入你的任务名称,按回车键确认' /></div>);}handleKeyUp = (e) => {// console.log(e.target.value, e.keyCode);const { keyCode, target } = e;if (keyCode !== 13) return; // 只能是按下回车键// 添加的名称不能为空if (target.value.trim() === '') {alert('输入不能为空');return;}const todoObj = { id: nanoid(), name: target.value, done: false };// 将todoObj传递给App组件中的addTodo函数this.props.addTodo(todoObj);// 清空输入target.value = '';};
}

四 TodoList 案例_勾选一个 todo

需要理解的概念有:

  • checked 与 defaultChecked 的差异与区别
  • 利用回调传递自定义参数与 event 事件参数
  • 多层组件嵌套时事件的传递与使用操作
  • 数组的遍历与数组对象的修改

src/components/Item/index.js

import React, { Component } from 'react';
import './index.css';
export default class Item extends Component {render() {const { id, name, done } = this.props;return (<li><label>{/*checkbox设置checked会报警告信息根据建议换成defaultChecked属性关于checked与defaultChecked属性的使用原则 :1.checked属性不能单独使用,时时刻刻起作用,必须配合onChanged和disabled使用,否则会有错误提示2.defaultChecked只控制初始渲染是否勾选*/}<input type='checkbox' checked={done} onChange={this.update(id)} /><span>{name}</span></label><button className='btn btn-danger' style={{ display: 'none' }}>删除</button></li>);}// 利用回调函数传递event事件// updateTodo的函数应该是最顶层的组件传递到上层组件,// 再由上层组件再传递给当前组件update = (id) => {return (e) => {this.props.updateTodo(id, e.target.checked);};};
}

Src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};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>);}// addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) => {const { todos } = this.state;// 追加一个todoconst newTodos = [todoObj, ...todos];// 更新状态this.setState({todos: newTodos,});};updateTodo = (id, done) => {console.log(id, done);// todos 从this.state进行解构赋值以后,不再是state的todosconst { todos } = this.state;// 修改todos也不是修改state里的todostodos.map((todoObj) => {if (todoObj.id === id) todoObj.done = done;return todoObj;});this.setState({ todos });};
}export default App;

src/components/List/index.js

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} id={todo.id} name={todo.name} done={todo.done} />;
// 利用展开式参数传递进行批量属性传递return <Item key={todo.id} {...todo} updateTodo={updateTodo} />;})}</ul>);}
}

五 不要直接修改 state 中的数据

需要理解的概念有:

  • 第 21 章节最后一句:状态数据,不能直接修改或更新,需要利用 setState 进行状态改新
  • 不能直接修改或者更新的意思还说的是不建议直接修改原来的 state 状态值数据
  • 对象拷贝如何实现

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};render() {// 解构赋值const { todos } = this.state;return (<div className='todo-container'><div className='todo-wrap'><Header /><List todos={todos} updateTodo={this.updateTodo} /><Footer /></div></div>);}updateTodo = (id, done) => {/*// 禁止使用非setState的形式去修改state中的数据// 如下代码就是典型的违反上述原则案例console.log(id, done);// todos 从this.state进行解构赋值以后,不再是state的todosconst { todos } = this.state;// 修改todos也不是修改state里的todostodos.map((todoObj) => {if (todoObj.id === id) todoObj.done = done;return todoObj;});this.setState({ todos });*/const todos = this.state.todos;const newTodos = todos.map((todoObj) => {if (todoObj.id === id) return { ...todoObj, done };return todoObj;});console.log(todos);console.log(newTodos);this.setState({ todos: newTodos });};
}export default App;

六 TodoList案例_高亮一个todo

需要理解的概念有:

  • 属性有效性验证的尝试
  • 事件绑定时event与自定义参数传递的两种形式
  • 鼠标的移入移出事件
  • 行内样式的动态绑定方式

src/components/Header/index.js

import React, { Component } from 'react';
import './index.css';
import { nanoid } from 'nanoid';
import PropTypes from 'prop-types';
// prop-types为默认安装模块,不需要再安装export default class Header extends Component {static propTypes = {addTodo: PropTypes.func.isRequired,todos: PropTypes.array.isRequired, // 添加todos将报以错误};render() {return (<div className='todo-header'><input type='text' onKeyUp={this.handleKeyUp} placeholder='请输入你的任务名称,按回车键确认' /></div>);}handleKeyUp = (e) => {// console.log(e.target.value, e.keyCode);const { keyCode, target } = e;if (keyCode !== 13) return; // 只能是按下回车键// 添加的名称不能为空if (target.value.trim() === '') {alert('输入不能为空');return;}const todoObj = { id: nanoid(), name: target.value, done: false };// 将todoObj传递给App组件中的addTodo函数this.props.addTodo(todoObj);// 清空输入target.value = '';};
}

src/components/Item/index.js

import React, { Component } from 'react';
import './index.css';export default class Item extends Component {state = {mouse: false,};render() {const { mouse } = this.state;const { id, name, done } = this.props;return (<listyle={{ backgroundColor: mouse ? '#ddd' : '#fff' }}onMouseEnter={() => this.handleMouse(true)}onMouseLeave={() => this.handleMouse(false)}><label>{/*checkbox设置checked会报警告信息根据建议换成defaultChecked属性*/}<input type='checkbox' checked={done} onChange={this.update(id)} /><span>{name}</span></label><button className='btn btn-danger' style={{ display: mouse ? 'block' : 'none' }}>删除</button></li>);}// 在事件绑定的时候利用箭头函数可以传递event与自定义参数// 不需要利用函数回调的形式传递event与自定义参数handleMouse = (flag) => {this.setState({ mouse: flag });};update = (id) => {return (e) => {this.props.updateTodo(id, e.target.checked);};};
}

七 TodoList案例_删除一个todo

需要理解的概念有:

  • 所有删除都需要确认
  • 更新状态需要复制对象
  • 事件绑定时event与自定义参数传递的两种形式
  • 属性类型与必要性约束

src/components/Item/index.js

import React, { Component } from 'react';
import './index.css';export default class Item extends Component {state = {mouse: false,};render() {const { mouse } = this.state;const { id, name, done } = this.props;return (<listyle={{ backgroundColor: mouse ? '#ddd' : '#fff' }}onMouseEnter={() => this.handleMouse(true)}onMouseLeave={() => this.handleMouse(false)}><label>{/*checkbox设置checked会报警告信息根据建议换成defaultChecked属性*/}<input type='checkbox' checked={done} onChange={this.update(id)} /><span>{name}</span></label><buttonclassName='btn btn-danger'onClick={() => this.handleDelete(id)}style={{ display: mouse ? 'block' : 'none' }}>删除</button></li>);}handleMouse = (flag) => {this.setState({ mouse: flag });};update = (id) => {return (e) => {this.props.updateTodo(id, e.target.checked);};};handleDelete = (id) => {if (window.confirm('确认删除吗?')) {this.props.deleteTodo(id);}};
}

src/components/List/index.js

import React, { Component } from 'react';
import Item from '../Item';
import './index.css';export default class List extends Component {render() {const { todos, updateTodo, deleteTodo } = this.props;return (<ul className='todo-main'>{todos.map((todo) => {// return <Item key={todo.id} id={todo.id} name={todo.name} done={todo.done} />;// 利用展开式参数传递进行批量属性传递return <Item key={todo.id} {...todo} updateTodo={updateTodo} deleteTodo={deleteTodo} />;})}</ul>);}
}

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};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>);}updateTodo = (id, done) => {/*// 禁止使用非setState的形式去修改state中的数据// 如下代码就是典型的违反上述原则案例console.log(id, done);// todos 从this.state进行解构赋值以后,不再是state的todosconst { todos } = this.state;// 修改todos也不是修改state里的todostodos.map((todoObj) => {if (todoObj.id === id) todoObj.done = done;return todoObj;});this.setState({ todos });*/const todos = this.state.todos;const newTodos = todos.map((todoObj) => {if (todoObj.id === id) return { ...todoObj, done };return todoObj;});console.log(todos);console.log(newTodos);this.setState({ todos: newTodos });};// addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) => {const { todos } = this.state;// 追加一个todoconst newTodos = [todoObj, ...todos];// 更新状态this.setState({todos: newTodos,});};deleteTodo = (id) => {// 获取原来的todosconst { todos } = this.state;// 删除指定id的对象const newTodos = todos.filter((todoObj) => {return todoObj.id !== id;});// 更新状态this.setState({ todos: newTodos });};
}export default App;

八 TodoList案例_清除所有已完成

需要理解的概念有:

  • 数组的filter过滤操作
  • 数组的其它操作函数的应用

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};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 deleteAllDoneTodo={this.deleteAllDoneTodo} /></div></div>);}updateTodo = (id, done) => {/*// 禁止使用非setState的形式去修改state中的数据// 如下代码就是典型的违反上述原则案例console.log(id, done);// todos 从this.state进行解构赋值以后,不再是state的todosconst { todos } = this.state;// 修改todos也不是修改state里的todostodos.map((todoObj) => {if (todoObj.id === id) todoObj.done = done;return todoObj;});this.setState({ todos });*/const todos = this.state.todos;const newTodos = todos.map((todoObj) => {if (todoObj.id === id) return { ...todoObj, done };return todoObj;});console.log(todos);console.log(newTodos);this.setState({ todos: newTodos });};// addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) => {const { todos } = this.state;// 追加一个todoconst newTodos = [todoObj, ...todos];// 更新状态this.setState({todos: newTodos,});};deleteTodo = (id) => {// 获取原来的todosconst { todos } = this.state;// 删除指定id的对象const newTodos = todos.filter((todoObj) => {return todoObj.id !== id;});// 更新状态this.setState({ todos: newTodos });};deleteAllDoneTodo = () => {const newTodos = this.state.todos.filter((todoObj) => !todoObj.done);this.setState({todos: newTodos,});};
}export default App;

src/components/Footer/index.js

import React, { Component } from 'react';
import './index.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 onClick={this.deleteAllDone} className='btn btn-danger'>清除已完成任务</button></div>);}deleteAllDone = () => {this.props.deleteAllDoneTodo();};
}

九 数组reduce方法

需要理解的概念有:

  • 数组reduce方法
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8" /><title>数组的reduce方法</title>
</head>
<body><script type="text/javascript">
//#region
/*
数组的reduce方法(很常用):
语法:arr.reduce((preValue, current, index, arr) => {} ,initialValue)preValue:第一次执行回调时为给定的初始值initialValue,以后是上一次执行回调时的返回值。备注:若没有传入initialValue,则第一次的preValue值是数组中第一个元素的值。current 表示当前正在处理的元素;initialValue 表示初始值。一般做数学运算时设置为0,若为筛选最值可以不传。arr:当前操作的数组index 表示当前正在处理的数组元素的索引,若传入了initialValue值,则为0,否则为1;
*/
//#endregionlet arr = [9, 1, 20, 3, 4, 5, 6, 7, 8, 9, 10]let persons = [{ id: '001', name: '老刘1', age: 18 },{ id: '001', name: '老刘2', age: 14 },{ id: '001', name: '老刘3', age: 15 },{ id: '001', name: '老刘4', age: 22 },]// const x = persons.reduce((pre,curr)=>{//     return pre + (curr.age > 15 ? 1 : 0)// },0)// console.log(x)// 做累加器使用----求出数组所有元素的和/* const x = arr.reduce((preValue,current)=>{// console.log(preValue,current)return preValue + current}) */// 条件统计 ---- 求出数组中偶数有几个/* const x = arr.reduce((preValue,current)=>{return preValue + (current % 2 === 0 ? 1 : 0 )},0) */// 条件求和 ---- 求出数组中所有偶数的和/* const x = arr.reduce((preValue,current)=>{return preValue + (current % 2 === 0 ? current : 0 )},0) */// 筛选最值/* const x = arr.reduce((preValue,current)=>{return Math.max(preValue,current)}) */</script>
</body></html>

十 TodoList案例_完成底部功能

需要理解的概念有:

  • 数组map遍历
  • 函数单行代码与多行代码的差异
  • 对象合并
  • checkbox状态值的获取
  • 函数属性的传递与接收使用

src/App.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import List from './components/List';
import './App.css';// 统一修改成类组件
class App extends Component {// 初始化状态state = {todos: [{ id: '001', name: '吃饭', done: true },{ id: '002', name: '睡觉', done: false },{ id: '003', name: '敲代码', done: true },],};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} /><FooterdeleteAllDoneTodo={this.deleteAllDoneTodo}todos={todos}updateAllTodo={this.updateAllTodo}/></div></div>);}updateTodo = (id, done) => {/*// 禁止使用非setState的形式去修改state中的数据// 如下代码就是典型的违反上述原则案例console.log(id, done);// todos 从this.state进行解构赋值以后,不再是state的todosconst { todos } = this.state;// 修改todos也不是修改state里的todostodos.map((todoObj) => {if (todoObj.id === id) todoObj.done = done;return todoObj;});this.setState({ todos });*/const todos = this.state.todos;const newTodos = todos.map((todoObj) => {if (todoObj.id === id) return { ...todoObj, done };return todoObj;});console.log(todos);console.log(newTodos);this.setState({ todos: newTodos });};// addTodo用于添加一个todo,接收的参数是一个todo对象addTodo = (todoObj) => {const { todos } = this.state;// 追加一个todoconst newTodos = [todoObj, ...todos];// 更新状态this.setState({todos: newTodos,});};deleteTodo = (id) => {// 获取原来的todosconst { todos } = this.state;// 删除指定id的对象const newTodos = todos.filter((todoObj) => {return todoObj.id !== id;});// 更新状态this.setState({ todos: newTodos });};deleteAllDoneTodo = () => {const newTodos = this.state.todos.filter((todoObj) => !todoObj.done);this.setState({todos: newTodos,});};updateAllTodo = (done) => {const newTodos = this.state.todos.map((todoObj) => {return { ...todoObj, done };});this.setState({todos: newTodos,});};
}export default App;

src/components/Footer/index.js

import React, { Component } from 'react';
import './index.css';
export default class Footer extends Component {render() {const { todos } = this.props;const doneCount = todos.reduce((pre, curr) => pre + (curr.done ? 1 : 0), 0);return (<div className='todo-footer'><label><inputtype='checkbox'checked={todos.length === doneCount && todos.length > 0}onChange={this.updateAll}/></label><span><span>已完成{doneCount}</span> / 全部{todos.length}</span><button onClick={this.deleteAllDone} className='btn btn-danger'>清除已完成任务</button></div>);}deleteAllDone = () => {this.props.deleteAllDoneTodo();};updateAll = (e) => {this.props.updateAllTodo(e.target.checked);};
}

十一 总结TodoList案例

需要理解的概念有:

(1)拆分组件时,要拆到位,结构、样式都要拆。
(2)组件化编码流程:1. 拆分组件: 根据功能抽取组件2. 实现静态组件: 使用组件实现静态页面效果3. 实现动态组件3.1 动态显示初始化数据3.1.1 数据类型3.1.2 数据名称3.1.2 保存在哪个组件?3.2 交互(从绑定事件监听开始)
(3)src/App.css放程序员写的公共样式
(4)public/css/bootstrap.css放成型的第三方公共样式
(5)关于react中<input type="checkbox">的checked属性与defaultChecked1.checked属性,时时刻刻起作用,而且必须配合disabled或onChange使用,否则就不能改了。2.defaultChecked属性,只起一次作用,可以单独使用 (不建议使用)
(6)关于父子之间通信:1.【父组件】给【子组件】传递数据:通过props传递2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
(7)使用//eslint-disable-next-line可以临时关闭下一行的语法检查
(8)熟练使用数组的reduce方法
{const { todos } = this.props;const doneCount = todos.reduce((pre, curr) => pre + (curr.done ? 1 : 0), 0);return (<div className='todo-footer'><label><inputtype='checkbox'checked={todos.length === doneCount && todos.length > 0}onChange={this.updateAll}/></label><span><span>已完成{doneCount}</span> / 全部{todos.length}</span><button onClick={this.deleteAllDone} className='btn btn-danger'>清除已完成任务</button></div>);}deleteAllDone = () => {this.props.deleteAllDoneTodo();};updateAll = (e) => {this.props.updateAllTodo(e.target.checked);};
}

十一 总结TodoList案例

需要理解的概念有:

(1)拆分组件时,要拆到位,结构、样式都要拆。
(2)组件化编码流程:1. 拆分组件: 根据功能抽取组件2. 实现静态组件: 使用组件实现静态页面效果3. 实现动态组件3.1 动态显示初始化数据3.1.1 数据类型3.1.2 数据名称3.1.2 保存在哪个组件?3.2 交互(从绑定事件监听开始)
(3)src/App.css放程序员写的公共样式
(4)public/css/bootstrap.css放成型的第三方公共样式
(5)关于react中<input type="checkbox">的checked属性与defaultChecked1.checked属性,时时刻刻起作用,而且必须配合disabled或onChange使用,否则就不能改了。2.defaultChecked属性,只起一次作用,可以单独使用 (不建议使用)
(6)关于父子之间通信:1.【父组件】给【子组件】传递数据:通过props传递2.【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
(7)使用//eslint-disable-next-line可以临时关闭下一行的语法检查
(8)熟练使用数组的reduce方法

react-TodoList案例相关推荐

  1. 基于vue的todolist案例

    前言:todolist案例真的算是经典了,不管你是学习原生js,还是学习jq,还是学习vue,亦或者是react,todolist是练习必不可少的练习demo了,下面我们来看看这个案例.需要完整代码的 ...

  2. Vue之Todolist案例和ES6语法

    2.7 Todolist案例 2.7.1 准备工作 <!DOCTYPE html> <html> <head><meta charset="utf- ...

  3. Vue.js-Day02-AM【Vue表单、核心指令(单选框、密码框、多行文本框、单选、多选、勾选、下拉列表)、组件汇总案例、ToDoList案例、计算属性(computed)、watch监听】

    Vue.js实训[基础理论(5天)+项目实战(5天)]博客汇总表[详细笔记] 目   录 1.Vue表单 原生JS实现异步表单提交 运行截图 代码 核心指令 单选框.密码框.多行文本框 单选 多选 勾 ...

  4. Vue版todolist案例

    Vue版todolist案例 todolist – 记录你的待办事项 <!DOCTYPE html> <html><head><meta charset=&q ...

  5. vue2.0-脚手架-todolist案例

    一 vue脚手架2.0 安装 npm install vue-cli -g 查阅一下脚手架可支持的模板 vue list可以查到 template-name 使用脚手架生成项目(以下命令得运行在项目的 ...

  6. 【Vue2.0】— TodoList案例(十七)

    [Vue2.0]- TodoList案例(十七) <template><div id="root"><div class="todo-con ...

  7. 尚硅谷todolist案例

    vue todolist案例 1 拆分组件 一共拆分为4个组件 TodoHeader TodoItem TodoList TodoFooter item是list的子组件 2 组件化编码流程 实现静态 ...

  8. ToDoList 案例完整 尚硅谷

    总结ToDoList案例: 1.组件化编码流程: (1).拆分静态组件:组件要按照功能点拆分,命名不要与html元素冲突. (2).实现动态组件:考虑好数据的存放位置,数据是一个组件再用,还是一些组件 ...

  9. React 基础案例 | 支持左右按钮点击查看信息的卡片组件(二)

    一.开篇 大家好,本篇文章小编将和大家一起实现一个用左右点击的方式翻看卡片信息的组件,这个组件很常用,一般会在网站上显示案例或团队成员的信息.通过本案例我们继续练习 useState Hook 在实际 ...

  10. React 基础案例 | 可折叠的问题列表和按分类展示的美食菜谱(三)

    一.开篇 大家好,本篇文章小编将和大家一起做两个简单的案例--可折叠的问题列表和按分类展示的美食菜谱.这两个案例,我们还是继续练习 useState Hook 的用法. 在前面的两篇文章里我们已经练习 ...

最新文章

  1. PostgreSQL连接池pgbouncer的使用
  2. pandas使用drop_duplicates函数基于subset参数指定的数据列子集删除重复行、并设置keep参数保留重复行中的最后一个数据行
  3. 各种好的开源项目-转载
  4. android之ListView和adapter配合显示图片和文字列表
  5. PyQt5 图形界面 - Qt Designer创建qrc资源文件引用图片资源实例演示,QTextBrower组件引用图片资源方法展示
  6. 业务决定功能,功能决定技术
  7. Linux 进程管理与监控(supervisor and monit)
  8. python创建多线程_初学者看过来:Python中多线程和多处理的指南
  9. python异常处理_Python 工匠: 异常处理的三个好习惯
  10. 中国联通最快明年实现2G全面退网;苹果发布iOS 14.3后火速撤回:原因未知;Angular 11.0.0 正式发布|极客头条
  11. 初学者应该如何开启自己的编程生涯?,nginx架构图
  12. C++调用.lib的方法
  13. 没有发现必备补丁文件‘NewopUI.pak’?
  14. 黑客X档案0911期PDF电子书
  15. 微信小程序-模拟器某些页面空白-引用 VantUI 后,页面空白
  16. 工作流——流程设计器
  17. FutureTask.get(timeOut)执行原理浅析
  18. linux系统是什么操作系统
  19. MacOS代理设置(桌面应用代理设置Terminal代理设置)
  20. Mac下matplotlib显示中文(不用安装字体)

热门文章

  1. 2019年信息安全工程师备考技巧
  2. python animation set data_imshow.set_数据()不适用于FuncAnimation
  3. 获得BootstrapTable行号index
  4. 2017-2018-1 20155339 《信息安全系统设计基础》第三周学习总结
  5. Linux统计目录下文件个数及代码行数
  6. SpringMVC前传--从Struts 1.x-2.x MVC-Spring 3.0 MVC
  7. 初步接触Oracle 11g(1)
  8. JavaScript 数组 array.at() 获取数组中最后一个元素
  9. Flutter ClipPath 自定义CustomClipper 玩转不一样的背景图案
  10. Android Animation --ScaleAnimation