前言
React是前端第一开发框架,是一个由 Facebook 开发的用于构建用户界面的 JavaScript 库,主要用于构建单页面应用程序(SPA)。React 的核心理念是将 UI 分解为可重用的组件,每个组件负责渲染和管理 UI 的一部分。这种组件化的方式使得代码更加模块化,易于维护和扩展。
使用React框架创建一个TodoList简陋demo
React官网并没有为我们过多的介绍它自己的方法的使用,大家如果有兴趣的话可以自行阅读。
1.分析功能需求
从图中我们可以看到有一个添加按钮,当我们点击添加按钮后,我们可以把输入框中的数据,显示在页面上,然后添加的数据后面紧跟一个编辑,删除按钮,编辑的功能是,当我们点击编辑时,我们可以修改该数据的值,点击删除就可以删除该数据。这就是我们这个简陋的demo的功能了。 看完之后我们就要开始划分组件了,首先,我们的父组件的作用是显示作为主页面,然后存在三个子组件,分别是表单Form部分,添加数据后List,List中的数据Item,三个子组件。
2.功能实现
首先我们应该创建一个React项目,我们在VsCode终端创建我们的React项目
//创建React项目文件 执行代码后会问两个问题第一个问题选React 第二个问题选JavaScript
npm init vite
//进入项目文件
cd 文件名
//下载项目依赖
npm i (install)
//执行项目
npm run dev
代码执行后,我们需要处理一些文件在src文件夹下的index.css的样式我们全部删除。对于css的样式,这里可以跟大家提几嘴,一般我们都是直接用
*{
padding: 0;
margin: 0;
}
去去除边距的,但是这样会导致页面加载的速度变慢,性能不好,以及使用
#list li{
}
这样写css也会导致代码性能下降,因为css选择器的匹配规则是从右到左的顺序来读取的,所以css选择器会选找到所有的li标签,再去找li中谁的id是#list,这样会大大降低页面加载的效率的。对于页面的性能优化,可以解读为,页面加载,首先是生成DOM树,然后渲染CSSOM树,然后读取script,所以我们一般script放在底部,以免导致页面堵塞,然后生成的DOM树+CSSOM树 = 渲染树,然后再生成布局树,最后再由GPU去绘制页面,形成静态页面。
打开我们的App.jsx文件,这是我们的父组件
import {Component} from 'react'
class App extends Component{
constructor(props){
super(props)//继承父类 将父类的构造函数执行一下
// 私有数据 申明自己的属性
this.state = {
todos:[]
}
//重写render()函数,一定要
render(){
return(
<div></div>
)
}
}
首先我们导入模块Componetn,然后我们使用es6中的模块化能力,使用传统面向对象的能力。在constructor()函数中,我们接收父类中的参数props,并使用super()函数调用。然后我们再私有化我们需要的数据,这里我私有化一个数据todos:[]用来存放我们的数据值。父组件中的第一步我们就完成了。然后我们再src文件夹下创建一个components文件夹,用来存放子组件,在components文件夹下,我们分别创建TodoForm.jsx/css,TodoList.jsx/css,TodoItem.jsx/css 一共六个文件。然后我们把子组件导入父组件中
import TodoForm from './components/TodoForm.jsx'
import TodoList from './components/TodoList.jsx'
然后在父组件中一共存在这几个函数adTodo()添加函数,deleteTodo()删除函数,toggleTodo()状态切换函数,editTodo()编辑函数。
// 修改状态 数据流
addTodo = (text)=>{
// Component 上有setState 方法,修改状态,响应式更新
this.setState({
todos:[
//展开用法
...this.state.todos,
{
text,
completed:false
}
]
})
}
// 数据编程
// 数据和界面状态是一一对应的
deleteTodo = (index)=>{
const newTodos = [...this.state.todos]
newTodos.splice(index,1),//删除某一项
this.setState({
todos:newTodos
})
}
toggleTodo = (index) =>{
const newTodos = [...this.state.todos]
newTodos[index].completed =!newTodos[index].completed
this.setState({
todos:newTodos
})
}
// 修改todo
editTodo = (index,newText)=>{
const newTodos = [...this.state.todos]
newTodos[index].text = newText
this.setState({
todos:newTodos
})
}
//抽象方法 abstract function
//每一个组件的界面不同
// 重写这个方法
render(){
return(
<div>
<div>
<TodoForm addTodo={this.addTodo}/>
<TodoList
todos={this.state.todos}
deleteTodo={this.deleteTodo}
toggleTodo={this.toggleTodo}
editTodo={this.editTodo}
/>
</div>
</div>
)
}
}
// 抛出
export default App;
最后被忘了抛出App;当然在几个子组件中也是需要抛出的不然在父组件中就无法导入.然后我们再通过数据流向由父-》子 子通过调用父props传过来的方法,通知数据修改,这样我们的父组件就基本完成。
2.1TodoFom.jsx
在TodoForm.jsx文件中我们只需要实现输入框以及添加就好了
代码的基本写法跟父组件其实是一样的,首先模块化class TodoForm extends Component 然后创建私有变量
constructor(props){
super(props);
this.state = {
inputText: '聚会'
}
}
我们重写render()方法,在这里我们可以写Form表单
render(){
return(
<form className="todo-form" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="请输入待办事项"
className="todo-form__input"
value={this.state.inputText}
onChange={this.handleChange}
/>
<button type="submit" className="todo-form__button">Add</button>
</form>
)
}
在React中写类名我们需要写className,这里我们给input框添加一个默认值value,是私有化变量this.state.inputText为“聚会”。然后添加一个名为Add的按钮,我们给表单添加函数onSubmit={this.handleSubmit}是当我们点击Add按钮后,把数据添加到List中,但是在这之前,我们会发现input框中的数据是无法修改的,原因是你的value值已经写死了为“聚会”,所以我们需要对其进行数据修改,当我们触发数据框数据修改时,原数据"聚会"就需要被替换成新的数据。
handleChange = (event)=>{
this.setState({
inputText: event.target.value
})
}
而添加函数,则是我们调用父组件传过来的props,去调用addTodo函数把数据把 this.state.inputText存入父组件中的this.state.最后不要忘了,添加完之后输入框中的数据记得清空
handleSubmit = (event)=>{
event.preventDefault();
if(this.state.inputText.trim()){
this.props.addTodo(this.state.inputText)
this.setState({
inputText: ''
})
}
}
但是这种情况,我们添加完数据后,刷新页面,就会导致数据没有被保存。因此我们可以在父组件中把子组件传过来的数据存到浏览器中,但是在React中我们又如何监听数据更新呢。 这里我们可以使用生命周期函数,进行数据存储
constructor(props){
super(props)//继承父类 将父类的构造函数执行一下
// 私有数据 申明自己的属性
const savedTodos = JSON.parse(localStorage.getItem("todos"))||[];
this.state = {
todos:[]
}
}
------------------------------------------
// 生命周期
componentDidUpdate(){
// console.log('updata....');
localStorage.setItem('todos', JSON.stringify(this.state.todos))
}
这里我们的constructor函数也需要相应的进行修改。
2.2TodoList.jsx
在TodoList.jsx文件中我们只需要遍历数据即可
import { Component } from "react";
import TodoItem from "./TodoItem";
import './TodoList.css'
class TodoList extends Component {
render(){
//解构父组件传过来的数据
const {todos,deleteTodo,toggleTodo,editTodo} = this.props;
return(
<div>
{
//遍历父组件传过来的参数
todos.map((todo,index)=>{
return <TodoItem
key={index}
index={index}
todo={todo}
deleteTodo={deleteTodo}
toggleTodo={toggleTodo}
editTodo={editTodo}
/>;
})
}
</div>
)
}
}
export default TodoList;
2.3TodoItem.jsx
在TodoItem.jsx文件中我们需要实现Item的删除,以及编辑, 删除无非就是点击删除按钮,触发父组件中的删除函数,这里我们重点介绍编辑。
首先,当我们点击编辑时,会出现一个新的输入框,然后隐藏原数据项
import { Component } from "react";
import './TodoItem.css'
class TodoItem extends Component{
constructor(props){
super(props);
this.state = {
isEditing:false,
editText:this.props.todo.text
}
}
handleEditChange = (event)=>{
this.setState({
editText:event.target.value
})
}
handleEditSave =()=>{
this.props.editTodo(this.props.index,this.state.editText)
this.setState({
isEditing:false
})
}
render(){
const {todo,deleteTodo,toggleTodo,editTodo,index} = this.props;
const {text,completed} = todo
return(
<li className={`todo-item ${completed ? 'todo-item--completed' : ''}`}>
{
this.state.isEditing?(
<div>
<input
type="text"
value={this.state.editText}
onChange={this.handleEditChange}
/>
<button onClick={this.handleEditSave}>保存</button>
</div>
):(
<div>
<span
className="todo-item__text"
onClick={()=>toggleTodo(index)}
>
{todo.text}
</span>
<button className="todo-item__edit-btn"
onClick={()=>this.setState({isEditing:true})}>编辑</button>
<button className="todo-item__delete-btn"
onClick={()=>deleteTodo(index)}>Delete</button>
</div>
)}
</li>
)
}
}
export default TodoItem;
当我们点击
<button className="todo-item__edit-btn"
onClick={()=>this.setState({isEditing:true})}>编辑</button>
编辑按钮时,就会触发函数把this.state中的isEncoding的值修改成true,当isEncoding的值变成true时,三元运算就会执行
this.state.isEditing?(
<div>
<input
type="text"
value={this.state.editText}
onChange={this.handleEditChange}
/>
<button onClick={this.handleEditSave}>保存</button>
</div>
):(
<div>
<span
className="todo-item__text"
onClick={()=>toggleTodo(index)}
>
{todo.text}
</span>
<button className="todo-item__edit-btn"
onClick={()=>this.setState({isEditing:true})}>编辑</button>
<button className="todo-item__delete-btn"
onClick={()=>deleteTodo(index)}>Delete</button>
</div>
)
切换成一个新的输入框。还有一个点是,当我们点击Item时,该Item就会被划掉
因为在 <li className={
todo-item ${completed ? 'todo-item--completed' : ''}}>
上面动态绑定了一个类,当点击时,就会触发函数toggleTodo(index),把completed取反,就添加类了。
这就是我今天为大家带来的一个简陋的todoList,若有不足,恳请各位指出!!!!