如何使用react创建一个实战小项目
背景
最近学了React组件化的思路,便有了做一个小项目的想法
看到桌面上的待办事项,便有了想法,做一个todolist
设计
对于react来说,分模块的开发React将应用程序分解为独立且可复用的组件。这种方法带来了诸多好处,包括但不限于:
模块化开发的好处
-
代码可读性和可维护性:
- 组件化使代码更加模块化,每个组件负责单一职责,这使得代码更容易理解和维护。
- 当功能发生变化或需要修改时,定位和更新相关的组件变得更加简单。
-
测试性:
- 单个组件易于单元测试,可以独立验证组件的功能和行为,确保其按预期工作。
-
性能优化:
- 通过使用虚拟DOM和只重新渲染发生更改的组件,React能够提高应用程序的性能。
- 分模块打包可以减少首屏加载时间,因为用户只加载他们当前需要的代码。
-
灵活性和扩展性:
- 模块化的组件可以轻松地添加、移除或替换,提高了系统的灵活性和适应性。
- 新的功能或设计可以更容易地集成到现有的应用程序结构中。
-
易于学习和上手:
- 对于新加入的开发人员,模块化组件使得系统更易于理解和上手,因为他们可以逐个组件地学习整个应用。
React中的模块化开发策略不仅提升了开发效率,还改善了代码质量,增强了应用程序的性能和可维护性。
好处这么多,那应该如何去设计好呢?
todoform组件去处理表单提交
你可以输入input框中在todoform组件handleChange
函数通过 event.target.value
获取用户的输入值,并使用 this.setState
来更新组件的 inputText
状态,从而使组件的状态与用户的输入保持同步。这个状态更新会触发组件的重新渲染,因此用户在界面上看到的输入框中的值会实时更新。
再创建应该todoitem组件去把需要的每一个需要做的todo去编写需要的功能
todoitem组件:
-
组件结构:
TodoItem
组件被设计为一个<li>
元素,包含了待办事项的文本和一些交互按钮。 -
数据传递:通过
props
从父组件接收必需的属性。这些属性包括:todo
:一个表示待办事项的对象,其中包含text
(事项文本)和completed
(完成状态)属性。index
:一个索引值,用于唯一标识每一个待办事项。toggleTodo
:一个函数,用于切换当前待办事项的完成状态。deleteTodo
:一个函数,用于从列表中删除当前待办事项。editTodo
:一个函数,用于保存当前待办事项的编辑内容。
-
状态管理:
TodoItem
组件使用React
的state
来管理本地状态。在构造函数中,初始化了两个状态属性:isEditing
:一个布尔值,指示当前待办事项是否正在被编辑。初始化为false
。editText
:一个字符串,保存正在编辑的文本内容。初始化为todo.text
。
-
事件处理器:为了处理用户的交互,定义了几个事件处理器:
handleEditChange
:处理编辑输入框的值变化事件,更新editText
状态。handleEditSave
:当用户点击“保留”按钮时,调用editTodo
函数,将编辑后的文本保存,并更新isEditing
状态为false
。
-
渲染逻辑:根据
isEditing
状态的不同,render
方法渲染不同的界面:- 当
isEditing
为false
:显示待办事项的文本,并提供 “编辑”、“删除” 按钮。文本被点击时调用toggleTodo
函数。 - 当
isEditing
为true
:显示一个输入框和一个 “保留” 按钮。输入框的值由editText
状态控制,且通过onChange
事件调用handleEditChange
。点击“保留”按钮时调用handleEditSave
。
- 当
todolist组件:
- 组件结构:整个 Todolist 组件由一个无序列表
ul
组成,每个列表项li
代表一个待办事项。列表项使用todo-item
CSS 类进行样式设置,并且通过添加或移除todo-item_completed
类来表示事项的完成状态。 - 数据传递:Todolist 组件接收一个
todos
属性,这是一个待办事项的数组。每个待办事项对象包含text
和completed
属性。text
属性显示在列表项的<span>
元素中,completed
属性用于应用样式。 - 方法传递:
toggleTodo
和deleteTodo
方法分别通过props传递给Todoitem
组件,以允许用户在列表项上进行交互操作。toggleTodo
方法用于切换单个事项的完成状态,deleteTodo
方法用于移除指定的事项。 - 遍历渲染:在
render
方法中,使用map
函数遍历todos
数组,为每个todo
对象创建一个Todoitem
组件实例。每个实例使用唯一的key
属性来确保 React 能够高效地更新和渲染。 - 交互性:每个
Todoitem
组件实例都渲染为一个<li>
元素,它包含<span>
来显示文本,以及三个<button>
按钮。用户可以点击<span>
文本区来切换完成状态(通过调用toggleTodo
)、点击删除
按钮来调用deleteTodo
,或者点击编辑
按钮来设置isEditing
状态为true
,从而进入编辑模式。 - 编辑模式:当
Todoitem
组件处于编辑模式(isEditing
为true
)时,它将显示一个输入框<input>
,允许用户编辑待办事项的文本。输入框的值由editText
状态控制,并通过handleEditChange
事件处理器更新。当用户点击保留
按钮时,handleEditSave
事件处理器将被调用,以保存编辑后的文本。 - 状态管理:在
constructor
中初始化组件的状态(isEditing
和editText
),并提供事件处理器(handleEditChange
和handleEditSave
)来管理编辑时的交互。
App组件
在App组件内去添加addTodo toggleTodo deleteTodo editTodo
事件处理函数,去添加管理todo的函数,把整体页面布局切出来
源代码
App.jsx
import React, { Component } from "react"
import Todolist from './components/todolist'
import Todoform from "./components/todoform"
import './App.css'
import Storage from "./utils/storage"
const instance = Storage.getInstance();
class App extends Component{
constructor(props){
super(props)
const savedtodos = JSON.parse(instance.getItem('todo'))||[];
this.state = {
todos:savedtodos
,
}
}
componentDidUpdate(){
instance.setItem('todo',JSON.stringify(this.state.todos))
}
addTodo = (text) => {
this.setState({
todos : [
...this.state.todos,
{
text,
completed:false,
}
]
})
}
toggleTodo = (index) => {
const newTodos = [...this.state.todos]
newTodos[index].completed = !newTodos[index].completed
this.setState({
todos: newTodos
})
}
deleteTodo = (index) => {
const newTodos = [...this.state.todos];
newTodos.splice(index,1);
this.setState({
todos:newTodos
})
}
editTodo = (index,text) => {
const newTodos = [...this.state.todos];//重新开辟一个新数组,重新去拿到相关的值,避免和之前的数据纠缠
newTodos[index].text = text;
this.setState({
todos:newTodos
})
}
render(){
const {todos} = this.state;
return(
<div className ='todo-app'>
<h1 className="todo-app__title">Todo List</h1>
<Todoform addTodo={this.addTodo}></Todoform>
<Todolist todos={todos}
toggleTodo={this.toggleTodo}
deleteTodo={this.deleteTodo}
editTodo={this.editTodo}></Todolist>
</div>
)
}
}
export default App
需要注意点:
1、 自定义React组件的类名和函数名应以大写字母开头。 在JSX中,React根据首字母是否大写来区分自定义组件和HTML原生元素。 以大写字母开头的名称被认为是React组件,如 < TodoForm />。以小写字母开头的名称被认为是HTML原生元素,如< div >。所以在这里面不能去创建< todoform >这种React不会识别成自定义组件
2、使用localStorage进行持久化存储,localStorage
是一个用于在浏览器端存储数据的对象。这些数据存储在用户的浏览器中,并且即使在浏览器关闭后,数据仍然保留着,除非用户手动清除浏览器缓存。这种数据存储方式非常适合需要持久化存储的场景,例如:记住用户的登录状态、保存用户的偏好设置等。
3、componentDidUpdate(){ instance.setItem('todo',JSON.stringify(this.state.todos)) }
在 React
中,componentDidUpdate
生命周期方法允许在组件更新后执行一些操作。在你的 App
组件中,componentDidUpdate
方法被用来在 todos
状态更新后,将最新的 todos
数据持久化到 localStorage
。
4、 Todoform组件传递addTodo方法:在渲染Todoform时,通过props将addTodo方法传递给它,以便子组件能够调用该方法添加新任务。 Todolist组件传递todos数据:将todos数组通过props传递给Todolist组件,以便在子组件中显示任务列表。
5、 修正class属性:在JSX中,使用className而不是class来定义CSS类名。
6、数据管理:Props是一种父组件向子组件传递数据的方式。它们是只读的,意味着子组件不能修改由父组件传递过来的props。
todoform.jsx
import React, { Component } from 'react';
import './todoform.css';
class TodoForm extends Component {
constructor(props) {
super(props);
this.state = {
inputText: ''
};
}
handleChange = (event) => {
this.setState({
inputText: event.target.value
});
};
handleSubmit = (e) => {
e.preventDefault();
if (this.state.inputText.trim()) {
this.props.addTodo(this.state.inputText);
this.setState({
inputText: ''
});
}
};
render() {
return (
<form className="todo-form" onSubmit={this.handleSubmit}>
<input
type="text"
value={this.state.inputText}
className="todo-form__input"
onChange={this.handleChange}
/>
<button type="submit" className="todo-form__button">
提交
</button>
</form>
);
}
}
export default TodoForm;
需要注意点:
1、this.props.addTodo(this.state.inputText); this.setState({ inputText: '' });
在你提交完表单数据后把输入框中的值添加完以后需要把输入框的值清空inputText: ''
todolist.jsx
import { Component } from "react";
import Todoitem from './todoitem.jsx'
import './todolist.css'
class todolist extends Component{
constructor(props){
super(props)
}
render(){
const { todos,
toggleTodo,//todoitem组件中需要用到toggleTodo方法
deleteTodo,
editTodo,
} = this.props
return(
<ul className="todo-list">
{todos.map((todo,index) =>(//遍历todos数组 回调函数中todo为数组中的元素 index为数组中的索引
<Todoitem
key={index}
todo={todo}
index={index}
toggleTodo={toggleTodo}
deleteTodo={deleteTodo}
editTodo={editTodo}></Todoitem>
))}
</ul>
)
}
}
export default todolist
todoitem.jsx
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,
index,
toggleTodo,
deleteTodo,
} = this.props
const { isEditing, editText } = this.state
const { text, completed, } = todo
// JS 运行区域
return (
<li className={`todo-item ${completed?'todo-item_completed':''}`} >
{isEditing?(
<div>
<input
type="text"
value={editText}
onChange={this.handleEditChange}
className="todo-item__Save"/>
<button className='todo-item__edit-input' onClick={this.handleEditSave}>保留</button>
</div>
):<div>
<span className='todo-item__text' onClick={() => toggleTodo(index)}>
{text}
</span>
<button className='todo-item__delete-btn'
onClick={() => deleteTodo(index)}>删除</button>
<button className='todo-item__edit-btn'
onClick={() => this.setState({isEditing: true})}>编辑</button>
</div>
}
</li>
)
}
}
export default TodoItem
storage.js
class Storage {
// static instance;
static getInstance() {
// JS 动态 static 属性
// JS 没有类,都是对象
if (!Storage.instance) {
Storage.instance = new Storage();
}
return Storage.instance;
}
getItem(key) {
return localStorage.getItem(key);
}
setItem(key, value) {
localStorage.setItem(key, value);
}
}
// new Storage();
export default Storage
这里使用localstorage 单例模式去持久化存储数据,这样在浏览器中会缓存数据,就算把页面关闭也不会导致数据丢失。单例模式是一种创建对象的模式,确保一个类只有一个实例,并提供一个全局访问点来访问这个实例。单例模式在许多库和框架中都有使用,特别是在需要维护全局状态或需要在整个应用程序中共享资源的情况下,这里用单例模式能保证只会创建一个Storage对象。
展示效果
在这个todolist里你可以提交你需要做的事情,编辑你需要做的事情,如果你做完了一件事情,你还可以点击事情让这个事情变成灰色,表示你已经做完,删除你不想做的事情。
我这边加了一些css样式,我的样式也不好看就不拿出来了,如果有需要可以私信我。
转载自:https://juejin.cn/post/7388025457822646282