👏欢迎来到我的React系列文章
🍭本系列文章从React入门开始,涵盖了React的一切基础,属于是从零开始的一个系列
🍭文章会以图文结合-动图-代码实现-解释代码的形式带领大家走进React的世界
🍭持续更新中~希望大家能够喜欢,系列文章👉React–从基础到实战
🌈博客主页👉codeMak1r的博客
👉关注✨点赞👍收藏📂
- 🔥React入门与概览(JSX语法)
- 🔥面向组件编程——组件实例的三大核心属性state、props和refs超详解
- 🔥受控组件与非受控组件(vue和react的双向绑定分别是怎么实现的?)
- 🔥React函数的柯里化(什么?这玩意儿不仅能装x,还能优化代码?)
- 🔥四行代码让我的cpu跑上90度——走进组件生命周期
- 🔥图文详解react组件生命周期——React v16.8
- 🔥react新生命周期图文详解——最新版
- 🔥react-getSnapshotBeforeUpdate()生命周期函数详解
- 🔥使用create-react-app(CRA)创建react项目
- 🔥react父子组件传值(通信)
- 🔥<前端组件化>拆分组件思想-吃饭、睡觉、打代码案例(附源码)(👈本文)
本例使用的是React v18.1技术栈
文章目录
🏆组件的组合使用-TodoList
功能: 组件化实现此功能
1.显示所有todo列表
2.输入文本,点击按钮显示到列表的首位,并清除输入的文本
▶️拆分组件
📎项目结构
📌index.js
import React from'react';import ReactDOM from'react-dom/client';import App from'./App';const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<React.StrictMode><App /></React.StrictMode>);
📌App.jsx
import React,{ Component }from'react';import Header from'./components/Header';import List from'./components/List';import Footer from'./components/Footer';import'./App.css'classAppextendsComponent{// 状态在哪里,操作状态的方法就在哪里
state ={todos:[{id:'001',name:'吃饭',done:true},{id:'002',name:'睡觉',done:true},{id:'003',name:'打代码',done:false},{id:'004',name:'逛街',done:true}]}// addTodo用于添加一个todo,接受的参数是todo对象addTodo=(todoObj)=>{const{ todos }=this.state
const newTodos =[todoObj,...todos]this.setState({todos: newTodos });}// 用于勾选和取消勾选todoupdateTodo=(id, done)=>{// 获取todosconst{ todos }=this.state
// 匹配处理数据const newTodos = todos.map((todoObj)=>{if(todoObj.id === id)return{...todoObj, done }elsereturn todoObj
})this.setState({todos: newTodos });}// 用于删除一个tododeleteTodo=(id)=>{const{ todos }=this.state
// 删除指定ID的todo对象const newTodos = todos.filter((todoObj)=>{// 数组的过滤方法,返回id不等于传进来的id值的那些todoObj对象,说明排除掉了点击了删除id对应的那个todoObjreturn todoObj.id !== id
})this.setState({todos: newTodos });}// 用于全选checkAllTodo=(done)=>{const{ todos }=this.state
const newTodos = todos.map((todoObj)=>{return{...todoObj, done }})this.setState({todos: newTodos });}// 用于清除已完成任务clearAllDone=()=>{const{ todos }=this.state
// 过滤数据const newTodos = todos.filter((todoObj)=>{return todoObj.done !==true})this.setState({todos: newTodos });}render(){return(<div className="todo-container"><div className="todo-wrap"><Header addTodo={this.addTodo}/><List todos={this.state.todos}
updateTodo={this.updateTodo}
deleteTodo={this.deleteTodo}/><Footer todos={this.state.todos}
checkAllTodo={this.checkAllTodo}
clearAllDone={this.clearAllDone}/></div></div>);}}exportdefault App;
📌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;}
Header部分
🛠Header-index.jsx
import React,{ Component }from'react';import PropTypes from'prop-types';import{ nanoid }from'nanoid'import'./index.css'classHeaderextendsComponent{// 键盘事件的回调handleKeyUp=(event)=>{if(event.key !=='Enter')return
console.log(event.target.value, event.keyCode, event.key)// 添加的todo名字不能为空if(event.target.value.trim()===''){alert('输入不能为空')return}const todoObj ={id:nanoid(),name: event.target.value,done:false}this.props.addTodo(todoObj)// 清空输入框
event.target.value =''}// 对接收的props进行类型限制static propTypes ={addTodo: PropTypes.func.isRequired,}render(){return(<div className="todo-header"><input onKeyUp={this.handleKeyUp} type="text" placeholder="请输入你的任务名称,按回车键确认"/></div>);}}exportdefault Header;
🛠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);}
List部分
🛠List-index.jsx
import React,{ Component }from'react';import PropTypes from'prop-types';import Item from'../Item';import'./index.css'classListextendsComponent{// 对接收的props进行类型限制static propTypes ={todos: PropTypes.array.isRequired,updateTodo: PropTypes.func.isRequired,deleteTodo: PropTypes.func.isRequired,}render(){const{ todos, updateTodo, deleteTodo }=this.props
return(<ul className="todo-main">{
todos.map(todo=>{return<Item key={todo.id}{...todo}
updateTodo={updateTodo}
deleteTodo={deleteTodo}/>})}</ul>);}}exportdefault List;
🛠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;}
Item部分
🛠Item-index.jsx
import React,{ Component }from'react';import'./index.css'classItemextendsComponent{// 标识鼠标移入、移出
state ={mouse:false}// 鼠标移入移出的回调handleMouse=(flag)=>{return()=>{this.setState({mouse: flag });}}// checkbox勾选的回调handleCheck=(id)=>{return(event)=>{
console.log(id, event.target.checked)this.props.updateTodo(id, event.target.checked)}}// 删除一个todo的回调handleDelete=(id, name)=>{return()=>{if(window.confirm('确定删除'+ name +'吗?')){this.props.deleteTodo(id)}}}render(){const{ id, name, done }=this.props
return(<li style={{backgroundColor:this.state.mouse ?'#ddd':'white'}}
onMouseEnter={this.handleMouse(true)}
onMouseLeave={this.handleMouse(false)}><label><input type="checkbox"
checked={done}
onChange={this.handleCheck(id)}/><span>{name}</span></label><button onClick={this.handleDelete(id, name)}
className="btn btn-danger"
style={{display:this.state.mouse ?'block':'none'}}>删除</button></li>);}}exportdefault Item;
🛠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;}
Footer部分
🛠Footer-index.jsx
import React,{ Component }from'react';import'./index.css'classFooterextendsComponent{// 全选checkbox的回调handleCheckAll=(event)=>{
console.log(event.target.checked)this.props.checkAllTodo(event.target.checked)}// 清除所有已完成任务的回调handleClearAllDone=()=>{this.props.clearAllDone()}render(){const{ todos }=this.props
// 已完成的个数const doneCount = todos.reduce((prev, currentTodo)=>{return prev +(currentTodo.done ?1:0)},0)// 总数const total = todos.length
return(<div className="todo-footer"><label><input type="checkbox"
checked={doneCount === total && total !==0?true:false}
onChange={this.handleCheckAll}/></label><span><span>已完成{doneCount}</span>/ 全部{total}</span><button onClick={this.handleClearAllDone}
className="btn btn-danger">清除已完成任务</button></div>);}}exportdefault Footer;
🛠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;}
✅案例总结
案例总结:
- 拆分组件,实现静态组件,注意:className、style的写法
- 动态初始化列表,如何确定将数据放在哪个组件的state中? - ——某个组件使用:放在自身state中- ——某些组件使用:放在他们共同的父组件state中(官方称此操作为:状态提升)
- 关于父子组件的通信:(👉父子组件通信详解) - 【父组件】给【子组件】传递数据:通过props传递- 【子组件】给【父组件】传递数据:通过props传递,要求父提前给子传递一个函数
- 注意
defaultChecked
和checked
的区别,类似的还有:defaultValue
和value
- 状态在哪里,操作状态的方法就在哪里
好啦~今天的案例分享就到这里了,如果有疑问或者文章出现错误的话请一定要联系我哟😎😎~
非常感谢你的阅读,你的支持将会是我最大的动力
👉关注✨点赞👍收藏📂
回见~
版权归原作者 codeMak1r. 所有, 如有侵权,请联系我们删除。