在这段时间,学习了一点React的知识,想着做个小demo来检验一下自己的学习成果,,各位看看有什么不足之处或者改进之处,还请帮忙批评指正一下,我包接受的,绝对不玻璃心(轻点,我骗人的🤐)。
做好前戏
那一开始肯定是先创建一个React的项目咯,运用很快的Vite工具来创建会很快的哦,尤其是在初始化打包的时候。
- 输入
npm init vite
,创建一个名为TodoList的项目,选择我们要使用的React框架,运用纯纯的JavaScript语言来编写 - 输入
cd todolist
跳转到该项目 - 然后输入
npm i
或者npm install
来安装项目需要用到的依赖 - 输入
npm run dev
来启动项目
思考思考
OK,前戏已经完成,开始对TodoList做一个分析:
- 既然用react框架,那肯定要运用他的组件化思想老,毕竟页面就是由组件构成的,说白了就是,你现在就是个老板,组件就是干活的,你把所有的事都丢给组件就完了
- 既然要用组件化思想,那肯定要想好要创建多少的组件老,简单来说就是什么,把你的事分为几块老,交给几个人去做
- 既然要创建组件,那肯定要想好每个组件是用来干什么的老,这不用说了吧,分配任务呗,职责分离
- 既然都确定好每个组件的任务,那就再确定好那个组件嵌套那个组件老,怎么说嵌套了,怎么说呢,你不是把任务分配下去了啊,那这些任务之间肯定有联系咯,这个人做好了什么东西,然后这些东西就可以传给下一个人去做去用,或者说也可以上一个人做了什么东西,把这个方法传给下一个人,告诉他昂,可以这样搞。
- 既然都这样了,那就开始思考思考老,把事拆分开来,那办事的效率不就高了,所以说React 的组件效能高,那是板上钉钉的事,谁来都没用。
- 既然这样,我想想用什么组件来搞老,要传授下一个人方法或者东西,这不就是继承嘛,那就用类组件呗
组件分类
好的好的,通过亿系列的思考,除去React本身具有的根组件,我就打算再创建三个组件:
- 第一个组件名为TodoFrom.jsx,里面呢,存放一个input框和一个提交按钮button,就负责输入数据和提交数据呗
- 第二个组件就叫TodoList.jsx吧,用来存放事件咯,所有的事件都传个他,放在里面就行了
- 第三个组件就叫TodoItem.jsx把,让他做TodoList的小秘书小助手把,把每个事件都管理好就完事。
- 那我呢,我干嘛,我这个老板还要干活啊,不干坚决不干,OK,App.jsx,你过来当我的小秘书,你去把任务区交给他们,然后最后再给我就行,叫他们把任务完成之后都交给你,你觉得ok再给我。ok了可以去睡大觉了。
App秘书干活
既然你要将任务交给下面的人,那就肯定得知道下面的人要干嘛,教教他们怎么做吧,这样他们干起来就快了,赶快完成任务,赶快交给老板。
-
首先,要知道秘书干活,最后也是要交给老板的,那秘书的口袋里就得有她的小本本吧(私有状态),小本本里写好之后再交给老板吧,既然秘书要想有自己的小本本,这不得要通过一下老板的同意?没有老板的同意,你还敢小动作?是吧,那么老板叫秘书干活,那肯定要知道是什么任务咯,那秘书就记录一下这个任务咯,在小本本上写上
Todo List
,那么就得这样子来干class App extends Component { constructor(props) { super(props) this.state={ todos:{ text:'干活干活干活', completed:false } } render() { const {todos} = this.state return( <div className="todo-app"> <h1 className="todo-app__title">Todo List</h1> </div> ) } }
-
老板都去睡觉了,那秘书就要开始自己的操作了,,首先给TodoFrom分配任务老。他要干嘛,他不就有个input框和button提交按钮嘛,那就给告诉他要怎么提交不就行了,传给他一个绝密配方呗。只要他这边一提交,就把数据交到秘书这里,在小本本是上修改一下存进去。就完了啊?昂,不然还要他干嘛?他这么强,其他的都不用教了。
addTodo = (text) => { this.setState({ todos:[ ...this.state.todos, { text, completed:false } ] }) }
-
然后就是给TodoList分配一点任务老,他要干嘛,他要对数据进行修改删除昂,然后需要把完成了的数据画根线给他标明一下老。不是,为什么TodoList什么都不会干?还要秘书教这么多,那没办法哦,秘书想和他天天交流咯
//删除
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
})
}
//修改
editTodo=(index,newText)=>{
const newTodos=[...this.state.todos]
newTodos[index].text=newText
this.setState({
todos: newTodos
})
}
OK了,秘书的任务干完了,把这些秘诀交给他也可以休息一下了
render() {
const {todos} = this.state
return(
<div className="todo-app">
<h1 className="todo-app__title">Todo List</h1>
<TodoFrom addTodo={this.addTodo}/>
<TodoList
todos={todos}
toggleTodo={this.toggleTodo}
editTodo={this.editTodo}
deleteTodo={this.deleteTodo}
/>
</div>
)
}
TodoFrom干活
- 任务到身上了,要怎么干了,怎么干??闭上眼睛就是冲,既然要把数据交给秘书,那肯定先要自己将数据整理好再交给她咯,秘书这么偏心,我也没办法老。秘书都不管我,放权了,那我也去拿个小本本来整理一下数据然后交给她咯
class TodoFrom extends Component {
constructor(props) {
super(props)
// 私有的状态
this.state = {
inputText: '',
}
}
- 秘书就给我一个方法去完成任务呀,那就自己发明一点方法出来用用呗。把input框里的数据写到自己的小本本上,然后再将小本本上的数据提交给秘书,,提交完之后将小本本,换一本新的。
handleChange=(event)=>{
this.setState({
inputText:event.target.value
})
}
handleSubmit=(e)=>{
//阻止事件的默认行为(阻止默认提交)
e.preventDefault()
//trim()方法用于删除字符串两端的空白字符,包括空格、制表符、换页符等不可见字符
if(this.state.inputText.trim()){
this.props.addTodo(this.state.inputText)
this.setState({inputText:''})
}
}
- OK的,那就用上自己发明的方法把,我也就这点本事了
render() {
return (
<form className='todo-From' onSubmit={this.handleSubmit}>
<input
type='text'
value={this.state.inputText}
className='todo-from__input'
onChange={this.handleChange}
/>
<button
type='submit'
className='todo-form__button'
> Add</button>
</form>
)
}
TodoList也干活吧
这要干嘛,秘书都贴身辅助了,啥也别干好了,把秘书给的东西都拿过来就好咯,然后再交给TodoItem小秘书呗,真羡慕啊,啥也不干,一天就完了。
class TodoList extends Component {
render() {
const {
todos,
deleteTodo,
editTodo,
toggleTodo
} =this.props
return (
<ul className='todo-list'>
{/* 循环输出 */}
{todos.map((todo,index)=>(
<TodoItem
key={index}
index={index}
todo={todo}
deleteTodo={deleteTodo}
toggleTodo={toggleTodo}
editTodo={editTodo}
/>
))}
</ul>
)
}
}
TodoItem要累死
上面一句话,底下累成狗。他不干,只能我干了,没办法,谁叫我是下面的人呢,别说了别说了累死了。 丢了几个方法交给我什么也不说就去约会了,我要怎么干啊。至少有个小本本有点甜头,不然是真的干不了一点。
- 那我肯定要自己判断哪些数据完成了需要画根线上去吧
- 然后呢然后呢,然后就展现默认状态呗,就显示上面给我的数据呗,然后再设置一下俩个按钮咯,一个修改按钮,一个删除按钮咯,然后把上面给我的删除的方法删除按钮咯,不过修改按钮呢,就给我一个修改方法,修改之后不要修改值啊,真的是服了
- 只能学一下TodoFrom了,自己造吧,关起门来往死里造呗,点击修改按钮就把默认状态改为修改状态哦,然后就出现input框和提交按钮咯,然后就提交咯,修改完就回到默认状态
class TodoItem extends Component {
constructor(props) {
super(props)
this.state = {
isEditing: false,
editText:this.props.todo.text
}
}
handleEditChange=(e)=>{
this.setState({
editText:e.target.value
})
}
handleEditSave=(e)=>{
this.props.editTodo(this.props.index, this.state.editText)
this.setState({
isEditing :false
})
}
render() {
const {todo,index,toggleTodo,deleteTodo,editTodo} = this.props
const {text,completed}=todo
const {editText,isEditing} = this.state
return (
<li className={`todo-item ${completed ? 'todo-item_completed': ''} `}>
{
isEditing ?
( <div>
<input
type="text"
className='todo-item__edit-input'
value={editText}
onChange={this.handleEditChange}
/>
<button
className='todo-item__save-btn'
onClick={this.handleEditSave}>
Save
</button>
</div>)
:
(<div>
<span
className='todo-item__text'
onClick={()=>toggleTodo(index)}>
{text}
</span>
<button
className='todo-item__edit-btn'
onClick={()=>this.setState({isEditing:true})}>
Edit
</button>
<button
className='todo-item__delete'
onClick={()=>deleteTodo(index)}>
delete
</button>
</div>)
}
</li>
)
}
}
秘书重造
好了,分配下去的任务都完成了,那秘书肯定要检查咯。诶,别说,一检查就出问题了,就是经不起检测,写好的数据,一刷新就啥也没有了,怎么办,秘书等下就要交给老板了,怎么办,怎么办,还能怎么办,凉拌。不干了,就干这一次,再也不干了。对哦,就干这一次,啥也不干了。秘书脑子一灵光,这不单例模式嘛,开搞开搞,
这找个工具,utils中创建一个工具Storage类,来实现单例模式:
-
单例模式实现:
Storage
类使用了单例模式,确保在整个应用中只存在一个实例。这通过getInstance
静态方法实现,它首先检查Storage.instance
是否已存在,如果不存在,则创建一个新的Storage
实例并将其赋值给Storage.instance
;如果已经存在,则直接返回该实例。这样可以避免多次实例化带来的资源浪费。
-
getItem
方法:- 这个方法接受一个键名参数
key
,然后调用浏览器的localStorage.getItem(key)
方法,返回与键名对应的存储值。
- 这个方法接受一个键名参数
-
setItem
方法:- 接受两个参数:键名
key
和值value
,然后调用浏览器的localStorage.setItem(key, value)
方法,将键值对存储到localStorage
中。这样可以将数据持久化,即使页面刷新或关闭后再次打开,数据仍然存在。
- 接受两个参数:键名
class Storage {
static getInstance() {
if (!Storage.instance) {
Storage.instance = new Storage();
}
return Storage.instance;
}
getItem(key) {
return localStorage.getItem(key);
}
setItem(key, value) {
localStorage.setItem(key, value);
}
}
export default Storage
那秘书的小本子上不得也改一改,利用了更新的生命周期函数componentDidupdate,局部DOM已经更新了,再去执行localStorage
const instance=Storage.getInstance();
class App extends Component {
constructor(props) {
super(props)
const saveTodos=JSON.parse(instance.getItem('todos'))||[]
this.state={
todos:saveTodos,
}
}
componentDidUpdate() {
instance.setItem('todos', JSON.stringify(this.state.todos))
}
OK了,秘书完成了任务哦,也是有点累了。就让老板来看看实现的效果吧
总结
做一个小小的demo,也是绞尽脑汁哦,人都没了,还是太菜了。说说总结吧,首先运用React框架去写项目,第一点我觉得就是要将自己的项目组件化,分出组件,并标明他们的作用,然后再去写。并且要注意,最好是所有的数据都交给秘书App,或者说是交给父组件,让父组件来管理,而子组件负责渲染就好咯,这样子数据就不会太乱。而且要注意数据和状态是要一一对应的,对应不上就完蛋。然后在使用单例模式,就算你退出来,或者更新了页面,里面的内容还会保存着,单例模式还是很好用的。不过还注意每个组件都要export出来哦,而且引入组件要import一下,Storage类也要export出来,利用好es6模块化的思想