当前位置: 首页 > news >正文

04 todoList案例

React全家桶

一、案例- TODO List 综合案例

在这里插入图片描述

功能描述

  1. 动态显示初始列表
  2. 添加一个 todo
  3. 删除一个 todo
  4. 反选一个 todo
  5. todo 的全部数量和完成数量
  6. 全选/全不选 todo
  7. 删除完成的 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} /><TodoFootercheckAllTodo={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');

相关文章:

04 todoList案例

React全家桶 一、案例- TODO List 综合案例 功能描述 动态显示初始列表添加一个 todo删除一个 todo反选一个 todotodo 的全部数量和完成数量全选/全不选 todo删除完成的 todo 1.1 静态组件构建 将资料包中的todos_page/index.html中核心代码添加到Todo.jsx文件中&#xff0c;…...

海睿思分享 | 浅谈企业数据质量问题

一、数据质量问题场景 在日常工作中&#xff0c;业务领导经常通过BI系统来了解各项业务的业绩情况。倘若某天&#xff0c;他打开某张核心报表&#xff0c;发现当日某个区域的数据一直是空白的。BI开发人员经过几个小时的排查分析&#xff0c;发现是当日该区域的销售数据存在产…...

神经网络:激活函数

在计算机视觉中&#xff0c;激活函数是神经网络中的一种非线性函数&#xff0c;用于引入非线性变换和非线性特性到网络中。激活函数的作用、原理和意义如下&#xff1a; 1. 引入非线性变换&#xff1a; 神经网络的线性组合层&#xff08;如卷积层和全连接层&#xff09;只能表…...

图像色彩增强相关论文阅读-Representative Color Transform for Image Enhancement(ICCV2021)

文章目录 Representative Color Transform for Image EnhancementAbstractIntroductionRelated workMethod实验Conclusion Representative Color Transform for Image Enhancement 作者&#xff1a;Hanul Kim1, Su-Min Choi2, Chang-Su Kim3, Yeong Jun Koh 单位&#xff1a;S…...

Elasticsearch介绍与应用

Elasticsearch介绍与应用 Elasticsearch的官方文档。 Elasticsearch官网参考文档&#xff1a;https://www.elastic.co/guide/index.html Elasticsearch官方下载地址&#xff1a;https://www.elastic.co/cn/downloads/elasticsearch mvnrepository依赖库地址&#xff1a;http…...

JavaEE规范

Servlet&#xff1a;用于开发 Web 应用程序的 API&#xff0c;定义了处理 HTTP 请求和响应的方式。JSP&#xff08;JavaServer Pages&#xff09;&#xff1a;一种在服务器端生成动态网页的技术&#xff0c;允许将 Java 代码嵌入到 HTML 页面中。(注意JSP本质就是一个Servlet)J…...

嵌入式实时操作系统的设计与开发New(八)

创建线程 用户在基于RTOS开发应用程序前&#xff0c;首先要创建线程。 用户创建一个线程时须指定用户希望采用的调度策略。 例如&#xff0c;用户想创建一个周期性执行的线程&#xff1a; acoral_period_policy_data_t* data; data acoral_malloc(sizeof(acoral_period_poli…...

MySQL事务相关笔记

杂项 InnoDB最大特点&#xff1a;支持事务和行锁&#xff1b; MyISAM不支持事务 介绍 一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元&#xff0c;只有当事务中的所有操作都正常执行完了&#xff0c;整个事务才会被提交给数据库。事务有如下特性…...

如何利用AI高效率快速调色

在设计行业中&#xff0c;时间是非常宝贵的资源&#xff0c;而设计师们常常需要应对繁忙的工作日程和紧迫的截止日期。为了提高工作效率和节省时间&#xff0c;越来越多的设计师开始利用人工智能&#xff08;AI&#xff09;技术中的高效调色功能。本文将介绍如何利用AI高效率快…...

数据结构--顺序表的基本操作--插入 and 删除

数据结构–顺序表的基本操作–插入 顺序表的插入操作 实现目标 ListInsert(&L,i,e):插入操作。在表L中的第i个位置上插入指定元素e。 typedef struct {int data[MaxSize];int len; }Sqlist;代码实现&#xff1a; #include <stdio.h> #include <stdlib.h> …...

BCSP-玄子Java开发之Java Web编程CH01_初识动态网页

BCSP-玄子Java开发之Java Web编程CH01_初识动态网页 1.1 B/S架构 B/S架构&#xff1a;浏览器/服务器 程序完全部署在服务器上使用浏览器访问服务器无需单独安装客户端软件 为什么要使用B/S架构 B/S与C/S比较B/S架构C/S架构软件安装浏览器需要专门的客户端应用升级维护客户…...

【软件教程】农林生环、水文、海洋、水环境、大气科学、人工智能、碳中和、碳排放、3S、R与统计等软件模型

本文涉及领域水文水资源、大气科学、农林生态、地信遥感、统计分析、编程语言等... 从软件基础到实践案例应用操作&#xff0c;手把手教学&#xff0c;提供永久回放观看和助学群长期辅助指导。适合课题组人员一站式学习&#xff0c;科研人员技术提升、企业单位工程项目、高校论…...

如何加入开源社

开源社成立于 2014 年&#xff0c;是由志愿贡献于开源事业的个人成员&#xff0c;依 “贡献、共识、共治” 原则所组成&#xff0c;始终维持厂商中立、公益、非营利的特点&#xff0c;是最早以 “开源治理、国际接轨、社区发展、项目孵化” 为使命的开源社区联合体。开源社积极…...

软件开发中的破窗效应

应该有很多人已经知道破窗效应【注1】这个社会学 &#xff08;犯罪学&#xff09;的词语&#xff0c;破窗效应最先由社会学家James Q. Wilson和George L. Kelling在一篇名为《Broken Windows》的文章中提出【注2】&#xff1a; “一个房子如果窗户破了&#xff0c;没有人去修补…...

机器视觉初步6-1:基于梯度的图像分割

把基于梯度的图像分割单独拿出来。 文章目录 一、图像梯度相关算子的原理1. Sobel算子2. Prewitt算子3. Roberts算子 二、python和halcon算子实现1.python实现2.halcon实现 基于梯度的图像分割方法利用像素之间的梯度信息来进行图像分割。 梯度 1是图像中像素灰度值变化最快的…...

从0开始,精通Go语言Rest微服务架构和开发

说在前面 现在拿到offer超级难&#xff0c;甚至连面试电话&#xff0c;一个都搞不到。 尼恩的技术社区中&#xff08;50&#xff09;&#xff0c;很多小伙伴凭借 “左手云原生右手大数据”的绝活&#xff0c;拿到了offer&#xff0c;并且是非常优质的offer&#xff0c;据说年…...

Sui x KuCoin Labs夏季黑客松|本周Workshop预告

自Sui x KuCoin Labs夏季黑客松推出以来已有四周的时间&#xff0c;期间收获了众多开发者的积极报名和热情参与。随着黑客松报名即将进入尾声&#xff0c;同期举办的Workshop也迎来了本周的最后一波。​本周的黑客松Workshop邀请到MoveEX和Mirror World的负责人作为嘉宾为大家带…...

从电源 LED 读取智能手机的秘密?

研究人员设计了一种新的攻击方法&#xff0c;通过记录读卡器或智能手机打开时的电源 LED&#xff0c;使用 iPhone 摄像头或商业监控系统恢复存储在智能卡和智能手机中的加密密钥。 众所周知&#xff0c;这是一种侧信道攻击。 通过密切监视功耗、声音、电磁辐射或执行操作所需…...

【Linux编辑器-vim使用】

目录 Linux编辑器-vim使用1.vim的基本概念2.vim的基本操作3.vim正常模式命令集4.vim末行模式命令集 Linux编辑器-vim使用 1.vim的基本概念 目前了解的vim有三种模式&#xff08;其实有好多模式&#xff09;&#xff0c;分别是命令模式、插入模式和底行模式&#xff0c;各模式…...

安装Apache mysql php

目录 一.Apache网站服务 Apache——》静态页面处理——》将静态处理交给PHP Apache简介 安装Apache服务 ​编辑 安装软件思路 二.安装mysql数据库 1. 安装依赖包 2.创建程序用户管理 3.加压安装包 这边就安装完成了​编辑 重点来了 报错了 没有空间 我最后的解决 方法…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

rknn优化教程(二)

文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK&#xff0c;开始写第二篇的内容了。这篇博客主要能写一下&#xff1a; 如何给一些三方库按照xmake方式进行封装&#xff0c;供调用如何按…...

聊聊 Pulsar:Producer 源码解析

一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台&#xff0c;以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中&#xff0c;Producer&#xff08;生产者&#xff09; 是连接客户端应用与消息队列的第一步。生产者…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)

可以使用Sqliteviz这个网站免费编写sql语句&#xff0c;它能够让用户直接在浏览器内练习SQL的语法&#xff0c;不需要安装任何软件。 链接如下&#xff1a; sqliteviz 注意&#xff1a; 在转写SQL语法时&#xff0c;关键字之间有一个特定的顺序&#xff0c;这个顺序会影响到…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

AI书签管理工具开发全记录(十九):嵌入资源处理

1.前言 &#x1f4dd; 在上一篇文章中&#xff0c;我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源&#xff0c;方便后续将资源打包到一个可执行文件中。 2.embed介绍 &#x1f3af; Go 1.16 引入了革命性的 embed 包&#xff0c;彻底改变了静态资源管理的…...

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...