网站建设要多少钱品牌,自己做广告图片什么软件免费,网站制作公司广州,微信的公众平台网站开发React全家桶
一、案例- TODO List 综合案例 功能描述 动态显示初始列表添加一个 todo删除一个 todo反选一个 todotodo 的全部数量和完成数量全选/全不选 todo删除完成的 todo 1.1 静态组件构建
将资料包中的todos_page/index.html中核心代码添加到Todo.jsx文件中#xff0c;…React全家桶
一、案例- TODO List 综合案例 功能描述 动态显示初始列表添加一个 todo删除一个 todo反选一个 todotodo 的全部数量和完成数量全选/全不选 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 classNametodo-containerdiv classNametodo-wrapdiv classNametodo-headerinput typetext placeholder请输入你的任务名称按回车键确认 //divul classNametodo-mainlilabelinput typecheckbox /spanxxxxx/span/labelbutton classNamebtn btn-danger删除/button/lililabelinput typecheckbox checked /span classNamedoneyyyy/span/labelbutton classNamebtn btn-danger删除/button/li/uldiv classNametodo-footerlabelinput typecheckbox //labelspanspan已完成0/span / 全部2/spanbutton classNamebtn 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 classNametodo-headerinput typetext 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 classNametodo-containerdiv classNametodo-wrapTodoHeader /.../div/div)
}1.2.2 拆分中间
在TODO文件夹中创建一个TodoMain文件夹将关于中间的组件代码进行拆分
import React from react
import ./TodoMain.css
export default function TodoMain() {return (ul classNametodo-mainlilabelinput typecheckbox /spanxxxxx/span/labelbutton classNamebtn btn-danger删除/button/lililabelinput typecheckbox checked /span classNamedoneyyyy/span/labelbutton classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader /TodoMain /..../div/div)
}1.2.3 拆分底部
在TODO文件夹中创建一个TodoFooter文件夹将关于底部的组件代码进行拆分
import React from react
import ./TodoFooter.css
export default function TodoFooter() {return (div classNametodo-footerlabelinput typecheckbox //labelspanspan已完成0/span / 全部2/spanbutton classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader /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 classNametodo-containerdiv classNametodo-wrapTodoHeader /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 classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn 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 classNametodo-headerinput typetextplaceholder请输入你的任务名称按回车键确认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 classNametodo-containerdiv classNametodo-wrapTodoHeader 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 classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done}onChange{changeDone} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader 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 classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn btn-danger删除/button/li})}/ul)
}
1.7 删除任务以及空状态提示
在子组件中给删除按钮添加点击事件
import React from react
import ./TodoMain.css
export default function TodoMain() {//省略前面代码...//声明删除功能函数let remove () {}return (ul classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader 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 classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn btn-danger onClick{remove(item.id)}删除/button/li})}/ul)
} 当将所有的任务全部删除后页面没有任何数据所以我们需要一个适当的提示 在子组件的虚拟DOM结构中添加一个li
return (ul classNametodo-main{todolists.map(item {return li key{item.id}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn btn-danger onClick{remove(item.id)}删除/button/li})}{todolists.length 0 li classNameempty暂无相关任务/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 classNametodo-footerlabelinput typecheckbox onChange{changeAll} //labelspanspan已完成0/span / 全部2/spanbutton classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader 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 classNametodo-footerlabelinput typecheckbox onChange{changeAll} //labelspanspan已完成0/span / 全部2/spanbutton classNamebtn 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 classNametodo-footerlabelinput typecheckbox onChange{changeAll}checked{todolists.every(item item.done) todolists.length 0}//labelspanspan已完成0/span / 全部2/spanbutton classNamebtn 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 classNametodo-footer//省略之前代码...button classNamebtn 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 classNametodo-containerdiv classNametodo-wrapTodoHeader 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 classNametodo-footerlabelinput typecheckbox onChange{changeAll}checked{todolists.every(item item.done) todolists.length 0}//labelspanspan已完成 {todolists.filter(item item.done).length}/span / 全部 {todolists.length}/spanbutton classNamebtn 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}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn 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 classNametodo-main{todolists.map(item {return TodoItem modifyTodo{modifyTodo} removeTodo{removeTodo} item{item} key{item.id} /})}{todolists.length 0 li classNameempty暂无相关任务/li}/ul)
}
子组件接收父组件传递的数据
import React from reactexport 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}labelinput typecheckbox checked{item.done}onChange{changeDone(item.id)} /span className{item.done ? done : }{item.title}/span/labelbutton classNamebtn 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 classNamehero-container containerh2 classNamepage-header英雄列表/h2div classNamerow{heros.map(item {return div classNamecol-xs-1 key{item.id}img classNameimg-responsive src{http://cdn.xiaohigh.com${item.big_image}} alt /p classNametext-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/todos1.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/httptry {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);