用React实战tolist

103 阅读9分钟

在这段时间,学习了一点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类,来实现单例模式:

  1. 单例模式实现

    • Storage类使用了单例模式,确保在整个应用中只存在一个实例。这通过getInstance静态方法实现,它首先检查Storage.instance是否已存在,如果不存在,则创建一个新的Storage实例并将其赋值给Storage.instance;如果已经存在,则直接返回该实例。这样可以避免多次实例化带来的资源浪费。
  2. getItem方法

    • 这个方法接受一个键名参数key,然后调用浏览器的localStorage.getItem(key)方法,返回与键名对应的存储值。
  3. 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了,秘书完成了任务哦,也是有点累了。就让老板来看看实现的效果吧

a4fad84d-a9a3-4990-b940-17e9a04627d3.gif

总结

做一个小小的demo,也是绞尽脑汁哦,人都没了,还是太菜了。说说总结吧,首先运用React框架去写项目,第一点我觉得就是要将自己的项目组件化,分出组件,并标明他们的作用,然后再去写。并且要注意,最好是所有的数据都交给秘书App,或者说是交给父组件,让父组件来管理,而子组件负责渲染就好咯,这样子数据就不会太乱。而且要注意数据和状态是要一一对应的,对应不上就完蛋。然后在使用单例模式,就算你退出来,或者更新了页面,里面的内容还会保存着,单例模式还是很好用的。不过还注意每个组件都要export出来哦,而且引入组件要import一下,Storage类也要export出来,利用好es6模块化的思想