React全家桶
一、案例- TODO List 综合案例
功能描述
- 动态显示初始列表
- 添加一个 todo
- 删除一个 todo
- 反选一个 todo
- todo 的全部数量和完成数量
- 全选/全不选 todo
- 删除完成的 todo
1.1 静态组件构建
- 将资料包中的todos_page/index.html中核心代码添加到Todo.jsx文件中,并将class修改成className。
- 创建todo.css样式文件,并在将资料包中的css文件中内容copy到该文件中,并导入
import React from 'react'
import "./todo.css"
export default function Todo() {
return (
<div className="todo-container">
<div className="todo-wrap">
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" />
</div>
<ul className="todo-main">
<li>
<label>
<input type="checkbox" />
<span>xxxxx</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
<li>
<label>
<input type="checkbox" checked />
<span className="done">yyyy</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
</ul>
<div className="todo-footer">
<label>
<input type="checkbox" />
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button className="btn btn-danger">清除已完成任务</button>
</div>
</div>
</div>
)
}
- 在将该组件导入到App.jsx中
import React from 'react'
import Todo from './TODO/Todo'
export default function App() {
return <Todo />
}
1.2 静态组件拆分
当某一个组件功能复用性非常高的时候,咱们需要考虑将其进行拆分成具体的组件进行复用,
在本实例中只是本着模块化的思想进行拆分。
1.2.1 拆分头部
- 在TODO文件夹中创建一个TodoHeader文件夹,将关于头部的组件代码进行拆分
import React from 'react'
import "./TodoHeader.css"
export default function TodoHeader() {
return (
<div className="todo-header">
<input type="text" placeholder="请输入你的任务名称,按回车键确认" />
</div>
)
}
- 将todo.css中关于header头部的样式也拆分出来,单独创建一个TodoHeader.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);
}
- 在Todo.jsx文件导入并调用组件
import React from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
export default function Todo() {
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader />
...
</div>
</div>
)
}
1.2.2 拆分中间
- 在TODO文件夹中创建一个TodoMain文件夹,将关于中间的组件代码进行拆分
import React from 'react'
import './TodoMain.css'
export default function TodoMain() {
return (
<ul className="todo-main">
<li>
<label>
<input type="checkbox" />
<span>xxxxx</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
<li>
<label>
<input type="checkbox" checked />
<span className="done">yyyy</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
</ul>
)
}
- 将todo.css中关于Main中间的样式也拆分出来,单独创建一个TodoMain.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;
}
/*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;
}
li .done {
color: #666;
text-decoration: line-through;
}
- 在Todo.jsx文件导入并调用组件
import React from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
export default function Todo() {
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader />
<TodoMain />
....
</div>
</div>
)
}
1.2.3 拆分底部
- 在TODO文件夹中创建一个TodoFooter文件夹,将关于底部的组件代码进行拆分
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter() {
return (
<div className="todo-footer">
<label>
<input type="checkbox" />
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button className="btn btn-danger">清除已完成任务</button>
</div>
)
}
- 将todo.css中关于Footer底部的样式也拆分出来,单独创建一个TodoFooter.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;
}
- 在Todo.jsx文件导入并调用组件
import React from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader />
<TodoMain />
<TodoFooter />
</div>
</div>
)
}
1.3 关于数据与状态的分析
首先,TodoMain组件中的数据肯定是循环显示在组件中,
而且还可以通过文本框输入提交进行修改和删除按钮一系列操作,
不难分析数据是变化的,那么咱们可以将这个数据的初始记录在state状态
中。
其次,状态中记录的值是一个数组
,里面每一个任务是一个对象
,里面的done用来标识是否选中状态
[
{id:1,title:'任务1',done:false},
{id:2,title:'任务2',done:false}
]
1.4 任务列表的数据渲染展示
- 在Todo.jsx文件中创建初始化状态值,并将状态值通过组件标签属性的方式传递给相应的子组件
export default function Todo() {
//记录状态
let [todolists, changetodos] = useState([
{ id: 1, title: '任务1', done: false },
{ id: 2, title: '任务2', done: true }
]);
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader />
<TodoMain todolists={todolists} />
<TodoFooter />
</div>
</div>
)
}
- 在TodoMain.jsx组件中接收父组件传递的属性属性,并进行循环渲染
import React from 'react'
import './TodoMain.css'
export default function TodoMain(props) {
//函数式组件中子组件接收父组件中的属性
//函数的参数名称自定义,只不过一般props含义为属性
//对象解构赋值
let { todolists } = props;
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
})
}
</ul>
)
}
1.5 新增任务功能
1.5.1 给Header子组件添加受控
- 在TodoHeader.jsx文件中添加受控组件,给文本框绑定onChange事件以及value属性
- 并添加onKeyDown事件,通过事件对象中的keyCode属性来判断用户按下的按键是什么
- 接收父组件传递的组件属性,用来修改父组件中的状态数据
import React, { useState } from 'react'
import "./TodoHeader.css"
export default function TodoHeader(props) {
let { addTodo } = props;
//声明状态
let [title, setTitle] = useState('')
let keyDown = (e) => {
//通过事件对象中的keyCode属性来判断,用户敲下的是回车键
if (e.keyCode === 13) {
// 新增一个任务
addTodo(e.target.value);
//清空文本框中的value值
setTitle('');
}
}
let changeValue = (e) => {
setTitle(e.target.value)
}
return (
<div className="todo-header">
<input type="text"
placeholder="请输入你的任务名称,按回车键确认"
onKeyDown={keyDown} value={title} onChange={changeValue} />
</div>
)
}
1.5.2 父组件传递属性到子组件
import React, { useState } from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
//记录状态
let [todolists, changetodos] = useState([
{ id: 1, title: '任务1', done: false },
{ id: 2, title: '任务2', done: true }
]);
let addTodo = (title) => {
changetodos([
...todolists,
{
id: todolists.length + 1,
title,
done: false
}
])
}
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader addTodo={addTodo} />
<TodoMain todolists={todolists} />
<TodoFooter />
</div>
</div>
)
}
1.6 点击任务复选框修改任务状态功能
-
先在子组件中添加onChange事件,确保程序运行没有报错,但是子组件没办法直接修改父组件中的数据,
仍然需要父组件通过调用子组件时通过属性进行传递
import React from 'react'
import './TodoMain.css'
export default function TodoMain(props) {
//函数式组件中子组件接收父组件中的属性
//函数的参数名称自定义,只不过一般props含义为属性
//对象解构赋值
let { todolists } = props;
let changeDone = () => {
}
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
})
}
</ul>
)
}
- 在父组件中添加一个修改状态的方法,并传递到子组件
import React, { useState } from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
//前面代码省略
...
//定义一个可以修改状态的方法
let modifyTodo = (id, done) => {
//注意:在这里咱们要修改状态时,在changetodos()里面需要传入一个修改状态后的新数组
//React会将原数组以及修改状态后的新数组进行比较
let newtodo = todolists.map(item => {
if (item.id === id) {
item.done = done;
}
return item;
})
changetodos(newtodo)
}
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader addTodo={addTodo} />
<TodoMain todolists={todolists} modifyTodo={modifyTodo} />
<TodoFooter />
</div>
</div>
)
}
- 子组件接收父组件传递的属性,并进行修改
import React from 'react'
import './TodoMain.css'
export default function TodoMain(props) {
//函数式组件中子组件接收父组件中的属性
//函数的参数名称自定义,只不过一般props含义为属性
//对象解构赋值
let { todolists, modifyTodo } = props;
let changeDone = (id) => {
return (e) => {
modifyTodo(id, e.target.checked);
}
}
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger">删除</button>
</li>
})
}
</ul>
)
}
1.7 删除任务以及空状态提示
- 在子组件中给删除按钮添加点击事件
import React from 'react'
import './TodoMain.css'
export default function TodoMain() {
//省略前面代码...
//声明删除功能函数
let remove = () => {
}
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger" onClick={remove}>删除</button>
</li>
})
}
</ul>
)
}
- 在父组件中将需要删除的项通过属性传递到子组件中
import React, { useState } from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
//省略前面代码...
//声明要删除的函数
let removeTodo = (id) => {
//利用数组中的过滤方法,将不满足传递的要删除的id值以外的所有的项过滤出来
let newtodo = todolists.filter(item => item.id !== id);
changetodos(newtodo)
}
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader addTodo={addTodo} />
<TodoMain todolists={todolists} removeTodo={removeTodo} modifyTodo={modifyTodo} />
<TodoFooter />
</div>
</div>
)
}
- 子组件根据父组件传递的属性进行接收,并执行
import React from 'react'
import './TodoMain.css'
export default function TodoMain(props) {
//函数式组件中子组件接收父组件中的属性
//函数的参数名称自定义,只不过一般props含义为属性
//对象解构赋值
let { todolists, modifyTodo, removeTodo } = props;
let changeDone = (id) => {
return (e) => {
modifyTodo(id, e.target.checked);
}
}
let remove = (id) => {
return (e) => {
removeTodo(id);
}
}
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger" onClick={remove(item.id)}>删除</button>
</li>
})
}
</ul>
)
}
-
当将所有的任务全部删除后,页面没有任何数据,所以我们需要一个适当的提示
在子组件的虚拟DOM结构中添加一个li
return (
<ul className="todo-main">
{
todolists.map(item => {
return <li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger" onClick={remove(item.id)}>删除</button>
</li>
})
}
{todolists.length === 0 && <li className='empty'>暂无相关任务</li>}
</ul>
)
在TodoMain.css中设置empty的相关样式
/* 删除 */
li.empty {
text-align: center;
padding: 20px 0;
}
1.8 全选与全不选的效果
1.8.1 点击全选按钮
- 先给子组件中的复选框绑定一个 onChange事件
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter() {
let changeAll = () => {
}
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={changeAll} />
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button className="btn btn-danger">清除已完成任务</button>
</div>
)
}
- 父组件声明函数,将其通过子组件属性进行传递
import React, { useState } from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
//省略之前代码...
//声明函数
let checkAllTodo = (done) => {
let newtodo = todolists.map(item => {
item.done = done;
return item;
})
changetodos(newtodo)
}
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader addTodo={addTodo} />
<TodoMain todolists={todolists} removeTodo={removeTodo} modifyTodo={modifyTodo} />
<TodoFooter checkAllTodo={checkAllTodo} />
</div>
</div>
)
}
- 子组件接收属性并调用
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter(props) {
let { checkAllTodo } = props;
let changeAll = (e) => {
checkAllTodo(e.target.checked);
}
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={changeAll} />
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button className="btn btn-danger">清除已完成任务</button>
</div>
)
}
1.8.2 点击每一项复选框选中全选框
- 父组件将状态数组传递到TodoFooter组件中
<TodoFooter checkAllTodo={checkAllTodo} todolists={todolists} />
-
子组件进行接收,当循环每一项中的复选框都选中了则全选框选中,反之不选中
并且考虑到当删除每一项之后,全选框也不应该选中,设定条件为数组长度不能为0
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter(props) {
let { checkAllTodo, todolists } = props;
let changeAll = (e) => {
checkAllTodo(e.target.checked);
}
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={changeAll}
checked={todolists.every(item => item.done) && todolists.length > 0}
/>
</label>
<span>
<span>已完成0</span> / 全部2
</span>
<button className="btn btn-danger">清除已完成任务</button>
</div>
)
}
- 实现底部任务统计效果
<span>已完成 {todolists.filter(item => item.done).length}</span> / 全部 {todolists.length}
1.9 移除已完成任务
- 给子组件中的按钮添加点击事件
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter() {
//省略之前代码...
let clear = () => {
}
return (
<div className="todo-footer">
//省略之前代码...
<button className="btn btn-danger" onClick={clear}>清除已完成任务</button>
</div>
)
}
- 父组件传递方法到子组件
import React, { useState } from 'react'
import "./todo.css"
import TodoHeader from './TodoHeader/TodoHeader'
import TodoMain from './TodoMain/TodoMain'
import TodoFooter from './TodoFooter/TodoFooter'
export default function Todo() {
//省略之前代码....
let removeAllTodo = () => {
let newtodo = todolists.filter(item => !item.done);
changetodos(newtodo)
}
return (
<div className="todo-container">
<div className="todo-wrap">
<TodoHeader addTodo={addTodo} />
<TodoMain todolists={todolists} removeTodo={removeTodo} modifyTodo={modifyTodo} />
<TodoFooter
checkAllTodo={checkAllTodo}
todolists={todolists}
removeAllTodo={removeAllTodo} />
</div>
</div>
)
}
- 子组件接收并执行
import React from 'react'
import "./TodoFooter.css"
export default function TodoFooter(props) {
let { checkAllTodo, todolists, removeAllTodo } = props;
let changeAll = (e) => {
checkAllTodo(e.target.checked);
}
let clear = () => {
removeAllTodo();
}
return (
<div className="todo-footer">
<label>
<input type="checkbox" onChange={changeAll}
checked={todolists.every(item => item.done) && todolists.length > 0}
/>
</label>
<span>
<span>已完成 {todolists.filter(item => item.done).length}</span> / 全部 {todolists.length}
</span>
<button className="btn btn-danger" onClick={clear}>清除已完成任务</button>
</div>
)
}
1.10 修复KEY重复的BUG
- npm i nanoid
- 在Todo.jsx文件中引入
import { nanoid } from 'nanoid'
- 修改添加任务时id的值,调用nanoid方法
{
id: nanoid(),
title,
done: false
}
1.11 封装TodoItem组件
- 在Todo文件夹下创建TodoItem子文件夹,并在其中创建TodoItem.jsx文件,将TodoMain循环项代码拆分
import React from 'react'
export default function TodoItem() {
return (
<li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger" onClick={remove(item.id)}>删除</button>
</li>
)
}
- TodoMain父组件中导入TodoItem子组件,并将需要的数据传递
import React from 'react'
import './TodoMain.css'
import TodoItem from '../TodoItem/TodoItem';
export default function TodoMain(props) {
//函数式组件中子组件接收父组件中的属性
//函数的参数名称自定义,只不过一般props含义为属性
//对象解构赋值
let { todolists, modifyTodo, removeTodo } = props;
return (
<ul className="todo-main">
{
todolists.map(item => {
return <TodoItem modifyTodo={modifyTodo} removeTodo={removeTodo} item={item} key={item.id} />
})
}
{todolists.length === 0 && <li className='empty'>暂无相关任务</li>}
</ul>
)
}
- 子组件接收父组件传递的数据
import React from 'react'
export default function TodoItem(props) {
let { modifyTodo, removeTodo, item } = props;
let changeDone = (id) => {
return (e) => {
modifyTodo(id, e.target.checked);
}
}
let remove = (id) => {
return (e) => {
removeTodo(id);
}
}
return (
<li key={item.id}>
<label>
<input type="checkbox" checked={item.done}
onChange={changeDone(item.id)} />
<span className={item.done ? 'done' : ""}>{item.title}</span>
</label>
<button className="btn btn-danger" onClick={remove(item.id)}>删除</button>
</li>
)
}
1.12 React中发送ajax请求
以英雄联盟数据列表为例
- 安装axios
npm i axios
- 创建一个Heros目录并添加Heros.jsx文件
import React from 'react'
import axios from 'axios'
import { useEffect } from 'react'
import { useState } from 'react'
export default function Hero() {
let [heros, setHeros] = useState([]);
//在组件挂载完成时执行
useEffect(() => {
/* axios.get('http://api.xiaohigh.com/heros').then(value => {
console.log(value.data);
}) */
async function main() {
let result = await axios.get('http://api.xiaohigh.com/heros');
console.log(result)
setHeros(result.data);
}
main()
}, [])
return (
<div className='hero-container container'>
<h2 className='page-header'>英雄列表</h2>
<div className="row">
{
heros.map(item => {
return <div className="col-xs-1" key={item.id}>
<img className='img-responsive' src={`http://cdn.xiaohigh.com${item.big_image}`} alt="" />
<p className='text-center'>{item.name}</p>
</div>
})
}
</div>
</div>
)
}
1.13 json-server启动服务
- 在当前案例项目中根目录创建一个server文件夹,并添加一个db.json文件,将之前初始化状态数据copy进来
{
"todos":[
{ "id": 1, "title": "任务", "done": false },
{ "id": 2, "title": "任务2", "done": true }
]
}
- 在server文件夹下终端运行服务
json-server --watch db.json --port 3001 #可以利用json-server --help来查看参数
注:如果使用json-server时报错,有可能是没有安装json-server
使用全局安装:npm i json-server -g
- 运行服务
http://localhost:3001/todos
1.14 发送请求列表展示所有任务数据
- 在Todo.jsx文件中导入axios
import axios from 'axios'
- 在导入useEffect
import React, { useEffect } from 'react'
- 调用函数,其内部完成ajax请求
//模拟ComponentDidMount
useEffect(() => {
async function main() {
try {
let result = await axios.get('http://127.0.0.1:3001/todos')
changetodos(result.data)
} catch (e) {
console.log('请求失败' + e.message)
}
}
main();
}, [])
1.15 新增任务并发送ajax请求
- 修改Todo.jsx中添加任务的方法代码为发送post请求
let addTodo = async (title) => {
try {
let result = await axios.post('http://127.0.0.1:3001/todos', {
id: nanoid(),
title,
done: false
})
changetodos([
...todolists,
result.data
])
} catch (e) {
console.warn('执行错误' + e.message)
}
}
1.16 axios封装简化url-baseURL
- 在src目录下新创建一个utils文件夹,并创建一个http.js文件
import axios from 'axios'
let instance = axios.create({
baseURL: 'http://127.0.0.1:3001'
})
export default instance;
- 修改Todo.jsx文件的导入以及请求的基础url
import axios from '../utils/http'
try {
let result = await axios.post('/todos', {
id: nanoid(),
title,
done: false
})
changetodos([
...todolists,
result.data
])
} catch (e) {
console.warn('执行错误' + e.message)
}
useEffect(() => {
async function main() {
try {
let result = await axios.get('/todos')
changetodos(result.data)
} catch (e) {
console.log('请求失败' + e.message)
}
}
main();
}, [])
1.17 显示进度条效果
- 安装进度条包
npm i nprogress
- 考虑到每一个请求中都会用到进度条效果,所以我们可以给axios中添加拦截器,修改http.js代码
import axios from 'axios'
import NProgress from 'nprogress'
import "nprogress/nprogress.css"
let instance = axios.create({
baseURL: 'http://127.0.0.1:3001'
})
instance.interceptors.request.use(config => {
NProgress.start();
return config;
})
instance.interceptors.response.use(response => {
NProgress.done();
return response;
})
export default instance;
1.18 错误的统一处理-中断Promise链条
- 在axios的响应拦截器中添加失败响应回调
instance.interceptors.response.use(response => {
NProgress.done();
return response;
}, error => {
console.log('请求响应失败')
console.log(error)
return new Promise(() => { });
})
- 在发送axios请求时,可以省去try…catch代码
1.19 发送请求修改复选框选中状态以及删除
let modifyTodo = async (id, done) => {
//注意:在这里咱们要修改状态时,在changetodos()里面需要传入一个修改状态后的新数组
//React会将原数组以及修改状态后的新数组进行比较
await axios.patch(`/todos/${id}`, { done })
let newtodo = todolists.map(item => {
if (item.id === id) {
item.done = done;
}
return item;
})
changetodos(newtodo)
}
let removeTodo = async (id) => {
await axios.delete(`/todos/${id}`)
let newtodo = todolists.filter(item => item.id !== id);
changetodos(newtodo)
}
1.20 批量更新状态与清空已完成任务
1.20.1 批量更新
let checkAllTodo = async (done) => {
//批量更新状态
let promises = todolists.map(item => {
//发送请求
return axios.patch(`/todos/${item.id}`, { done });
})
try {
await Promise.all(promises);
let newtodo = todolists.map(item => {
item.done = done;
return item;
})
changetodos(newtodo)
} catch (e) {
console.log('更新失败' + e.message);
}
}
1.20.2 清空已完成任务
let removeAllTodo = async () => {
//发送请求
let promises = todolists.filter(item => item.done).map(item => {
return axios.delete(`/todos/${item.id}`)
})
try {
await Promise.all(promises);
let newtodo = todolists.filter(item => !item.done);
changetodos(newtodo)
} catch (e) {
console.log('删除失败' + e.message)
}
}
1.21 toastify操作提醒
- 安装
npm i toastify
- 导入
import { ToastContainer, toast } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
- 在需要提示的位置添加
- 成功
toast.success('xxx');
- 失败
toast.error('xxx');