在原有项目基础上实现:
- enter键提交输入框内容
- 点击添加键后聚焦输入框
- 实现删除任务
- 实现任务状态切换
- 实现任务数目统计
- bulma界面美化
- classnames判断条件添加className
- prop-types数据检测
在项目目录下安装bulma,prop-types,classnames
命令:cnpm install bulma prop-types classnames --save
在项目public目录下的index.html文件中引入bulma的css
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css">
代码:
在src目录下:
index.js
import React from 'react';
import ReactDom from 'react-dom';
import TodoApp from './TodoApp';
//渲染TodoApp内的内容到id位root的选择器
ReactDom.render(
<TodoApp />,
document.querySelector("#root")
)
TodoApp.js
import TodoHeader from './component/TodoHeader';//引入TodoHeader组件
import TodoInput from './component/TodoInput';//引入TodoInput组件
import TodoList from './component/TodoList';//引入TodoList组件
import TodoFooter from './component/TodoFooter';//引入TodoFooter组件
import React, { Component } from 'react'
//TodoApp组件
export default class TodoApp extends Component {
//定义state存储list中要显示的数据
constructor(){
super();
this.state = {
todos: [{
id: Math.random(),
text: '哈哈',
iscompleted: true
}, {
id: Math.random(),
text: '大大',
iscompleted: false
}, {
id: Math.random(),
text: '小小',
iscompleted: true
}]
}
}
//addList放啊对Input输入数据进行处理
addToList = (text)=>{
this.setState({//利用setState改变state的值
todos:[//对所有的todos数据操作
...this.state.todos,
{
text,//text:text,将Input内输入的值写入state的todos
id: Math.random(),//id取随机数
iscompleted: true//将完成状态默认设置为true
}
]
})
}
//根据id对List数据进行删除的方法
ondeleteItem=(id)=>{
//过滤掉传入的id值所指定的数据
/*const todos = this.state.todos.filter(todo=>{//找到所有的与传入id不同的数据
return todo.id !=id;
});*/
//简写过滤
const todos = this.state.todos.filter(todo=>todo.id!==id);
this.setState({
todos//todos:todos将剩下的数据再赋值给state的todos
})
}
//根据id对list数据进行完成状态修改
onchangeStatus=(id)=>{
const todos = this.state.todos.map(todo=>{
//找到需要修改状态的数据
if(todo.id===id){
//将该条数据中的iscomplited的布尔值取反
todo.iscompleted = ! todo.iscompleted;
}
return todo;
});
this.setState({
todos
});
}
//渲染显示TodoHeader、TodoInput、TodoList等组件
render() {
//定义变量保存总任务数量(state中todos的长度)
const totalCount = this.state.todos.length;
//定义变量保存未完成的任务数(过滤iscomplited!==true后返回的数组的长度)
const uncompletedCount = this.state.todos.filter(todo=>todo.iscompleted!==true).length;
//定义变量保存已完成的任务数(过滤iscomplited===true后返回的数组的长度)
const completedCount = this.state.todos.filter(todo=>todo.iscompleted===true).length;
return (
<div>
<TodoHeader />
<section className="hero is-light">
<div className="hero-body">
<div className="container">
{/* 将addToList数据处理函数传出去 */}
<TodoInput addToList={this.addToList}/>
{/* 通过state内的数据控制List的显示 */}
<TodoList todos={this.state.todos} ondeleteItem={this.ondeleteItem} onchangeStatus={this.onchangeStatus}/>
{/* 渲染显示Footer中的数值 */}
<TodoFooter totalCount={totalCount} uncompletedCount={uncompletedCount} completedCount={completedCount} />
</div>
</div>
</section>
</div>
)
}
}
src目录的component目录下:
TodoHeader.js
/*
//第一种TodoHeader组件定义方法
import React from 'react'
//TodoHeader组件
const TodoHeader = ()=>{
return (
<div>
<h2>点击按钮添加输入框内容到列表</h2>
</div>
)
}
//导出TodoHeader
export default TodoHeader;*/
//第二种TodoHeader组件定义方法
import React, { Component } from 'react'
export default class TodoHeader extends Component {
render() {
return (
<section className="hero is-primary">
<div className="hero-body">
<div className="container">
<h1 className="title">
待办事项列表
</h1>
<h2 className="subtitle">
将输入框中的输入显示到表格并进行操作
</h2>
</div>
</div>
</section>
)
}
}
TodoInput.js
import React, { Component , createRef} from 'react'
//TodoInput组件
export default class TodoInput extends Component {
//添加数据管理state保存input内的值
constructor(){
super();
this.state={inputValue:''};
this.inputRef= createRef();
}
//每次输入完成调用
changeHandler=(e)=>{
this.setState({//将当前input输入的数据存入state的inputValue
inputValue: e.currentTarget.value//e.currentTarget当前改变事件指向的input输入框
})
}
//每次输入之后点击添加按钮调用
clickHandler=()=>{
//当input输入框为空时,无法实现添加
if(this.state.inputValue ===''){
return;
}
//调用addToList数据处理函数对当前inputValue里的值进行处理
this.props.addToList(this.state.inputValue);
//通过setState设置state的inputValue的值
this.setState({
//将inputValue的值设置为空字符串
inputValue:''
});
//点击添加后保持input输入框聚焦
this.inputRef.current.focus();
}
//触发enter键处理提交数据
keyupHandler=(e)=>{
//当触发enter键
if(e.keyCode===13){
//调用添加按钮的处理函数实现与点击按钮相同后的相同效果
this.clickHandler()
}
}
render() {
return (
// <div>
// {/*onChange={this.changeHandler}当输入框数据有变动时调用函数changeHandler将输入的数据放入inputValue */}
// {/*value={this.state.inputValue}在将输入的值放入inputValue之后,再次从state里面取出inputValue的值再放入输入框 */}
// <input type="text" ref={this.inputRef} onChange={this.changeHandler} value={this.state.inputValue} onKeyUp={this.keyupHandler}/>
// {/*onClick = {this.clickHandler}当处理完输入框的数据后,调用clickHandler函数对input的值进行加入List的处理 */}
// <button onClick = {this.clickHandler}>添加</button>
// </div>
<div className="field has-addons">
<div className="control">
<input type="text" className="input" ref={this.inputRef} onChange={this.changeHandler} value={this.state.inputValue} onKeyUp={this.keyupHandler}/>
</div>
<div className="control">
<button className="button is-primary" onClick = {this.clickHandler}>添加</button>
</div>
</div>
)
}
}
TodoList.js
import React, { Component } from 'react';
import classNames from 'classnames';//引入classnames
import PropTypes from 'prop-types';//引入prop-types
//TodoList组件
export default class TodoList extends Component {
/*//方法三:在constructor中改变this指向
constructor(){
super();
//改变this指向,避免性能问题
this.deleteHandler = this.deleteHandler.bind(this);
}*/
//props数据检测
static propTypes = {
todos: PropTypes.arrayOf(//数组检测
PropTypes.shape({//对象检测
id: PropTypes.number.isRequired,//数字检测
text: PropTypes.string.isRequired,//字符串检测
iscompleted: PropTypes.bool.isRequired//布尔值检测
}).isRequired
).isRequired
}
deleteHandler(id){
//调用ondeleteItem传递需要删除的id
this.props.ondeleteItem(id);
}
changeStatus(id){
//调用onchangeStatus传递需要更改状态的id
this.props.onchangeStatus(id);
}
render() {
return (
<table className="table is-striped">
<thead>
<tr>
<th>id</th>
<th>任务</th>
<th>完成状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{//利用map实现循环,对todos里的数据进行遍历,每一条数据均为一个todo
this.props.todos.map(todo => {//每条数据返回显示一条带有自身(todo)的数据的行
//将判断条件todo.iscompleted===true用一个变量iscompleted保存下来
const iscompleted = todo.iscompleted===true;
return (
<tr key={todo.id}>
<td>{todo.id}</td>
<td>{todo.text}</td>
<td>
{/* //当isCompleted为true时显示已完成,否则显示未完成 */}
{/* {todo.iscompleted === true ?<span>已完成</span>:<span>未完成</span>} */}
{/* 优化 利用classnames的classNames方法对span元素的className进行判断处理,当满足iscompleted(即todo.iscompleted===true)时,
添加className为is-primary且显示已完成,不满足时,添加className为is-warning且显示未完成*/}
<span className={classNames("tag",{
"is-primary" : iscompleted,
"is-warning" : !iscompleted
})}>{iscompleted? "已完成":"未完成"}</span>
</td>
<td>
<div className="buttons has-addons">
{/*方法一:this.deleteHandler.bind(this,todo.id)利用bind不执行函数情况下改变this指向,并将id传递过来 */}
<button className="button is-warning" onClick={this.deleteHandler.bind(this, todo.id)}>删除</button>
{/*方法二:利用箭头函数解决this指向问题,直接调用deleteHandler函数并将id作为参数传递 */}
{/* <button ondelete={()=>{this.deleteHandler(todo.id)}}>删除</button> */}
<button className="button is-primary" onClick={this.changeStatus.bind(this, todo.id)}>切换状态</button>
</div>
</td>
</tr>
)
})
}
</tbody>
</table>
)
}
}
TodoFooter.js
import React, { Component } from 'react';
import PropTypes from 'prop-types';//引入prop-types
export default class TodoFooter extends Component {
//定义静态的propTypes对props中的数据进行检测totalCount: PropTypes.number.isRequired(totalCount是必须有输入/存在的数据类型)
static propTypes = {
totalCount: PropTypes.number.isRequired,
uncompletedCount: PropTypes.number,
completedCount: PropTypes.number
}
render() {
//从props中解构出totalCount,completedCount,uncompletedCount
const{totalCount,completedCount,uncompletedCount} = this.props
return (
<div>
<div>总任务:{totalCount}</div>
<div>已完成任务:{uncompletedCount}</div>
<div>未完成任务:{completedCount}</div>
</div>
)
}
}
网友评论